blob: d6f09e855e3b14182e7bc6d9165067a173bf8a62 [file] [log] [blame]
Brian Silvermancc09f182022-03-09 15:40:20 -08001# Copyright 2015 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://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,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Adam Snaider1c095c92023-07-08 02:09:58 -040015"""Rust rule implementations"""
16
Brian Silverman5f6f2762022-08-13 19:30:05 -070017load("@bazel_skylib//lib:paths.bzl", "paths")
Brian Silvermancc09f182022-03-09 15:40:20 -080018load("//rust/private:common.bzl", "rust_common")
Adam Snaider1c095c92023-07-08 02:09:58 -040019load("//rust/private:providers.bzl", "BuildInfo")
Brian Silvermancc09f182022-03-09 15:40:20 -080020load("//rust/private:rustc.bzl", "rustc_compile_action")
21load(
22 "//rust/private:utils.bzl",
Brian Silverman5f6f2762022-08-13 19:30:05 -070023 "can_build_metadata",
Brian Silvermancc09f182022-03-09 15:40:20 -080024 "compute_crate_name",
Adam Snaider1c095c92023-07-08 02:09:58 -040025 "crate_root_src",
Brian Silvermancc09f182022-03-09 15:40:20 -080026 "dedent",
27 "determine_output_hash",
28 "expand_dict_value_locations",
29 "find_toolchain",
30 "get_import_macro_deps",
31 "transform_deps",
32)
Brian Silvermancc09f182022-03-09 15:40:20 -080033# TODO(marco): Separate each rule into its own file.
34
35def _assert_no_deprecated_attributes(_ctx):
36 """Forces a failure if any deprecated attributes were specified
37
38 Args:
39 _ctx (ctx): The current rule's context object
40 """
41 pass
42
43def _assert_correct_dep_mapping(ctx):
44 """Forces a failure if proc_macro_deps and deps are mixed inappropriately
45
46 Args:
47 ctx (ctx): The current rule's context object
48 """
49 for dep in ctx.attr.deps:
50 if rust_common.crate_info in dep:
51 if dep[rust_common.crate_info].type == "proc-macro":
52 fail(
53 "{} listed {} in its deps, but it is a proc-macro. It should instead be in the bazel property proc_macro_deps.".format(
54 ctx.label,
55 dep.label,
56 ),
57 )
58 for dep in ctx.attr.proc_macro_deps:
59 type = dep[rust_common.crate_info].type
60 if type != "proc-macro":
61 fail(
62 "{} listed {} in its proc_macro_deps, but it is not proc-macro, it is a {}. It should probably instead be listed in deps.".format(
63 ctx.label,
64 dep.label,
65 type,
66 ),
67 )
68
69def _determine_lib_name(name, crate_type, toolchain, lib_hash = None):
70 """See https://github.com/bazelbuild/rules_rust/issues/405
71
72 Args:
73 name (str): The name of the current target
74 crate_type (str): The `crate_type`
75 toolchain (rust_toolchain): The current `rust_toolchain`
76 lib_hash (str, optional): The hashed crate root path
77
78 Returns:
79 str: A unique library name
80 """
81 extension = None
82 prefix = ""
83 if crate_type in ("dylib", "cdylib", "proc-macro"):
84 extension = toolchain.dylib_ext
85 elif crate_type == "staticlib":
86 extension = toolchain.staticlib_ext
87 elif crate_type in ("lib", "rlib"):
88 # All platforms produce 'rlib' here
89 extension = ".rlib"
90 prefix = "lib"
91 elif crate_type == "bin":
92 fail("crate_type of 'bin' was detected in a rust_library. Please compile " +
93 "this crate as a rust_binary instead.")
94
95 if not extension:
96 fail(("Unknown crate_type: {}. If this is a cargo-supported crate type, " +
97 "please file an issue!").format(crate_type))
98
99 prefix = "lib"
Adam Snaider1c095c92023-07-08 02:09:58 -0400100 if toolchain.target_triple and toolchain.target_os == "windows" and crate_type not in ("lib", "rlib"):
Brian Silvermancc09f182022-03-09 15:40:20 -0800101 prefix = ""
102 if toolchain.target_arch == "wasm32" and crate_type == "cdylib":
103 prefix = ""
104
105 return "{prefix}{name}{lib_hash}{extension}".format(
106 prefix = prefix,
107 name = name,
108 lib_hash = "-" + lib_hash if lib_hash else "",
109 extension = extension,
110 )
111
Brian Silverman5f6f2762022-08-13 19:30:05 -0700112def get_edition(attr, toolchain, label):
Brian Silvermancc09f182022-03-09 15:40:20 -0800113 """Returns the Rust edition from either the current rule's attirbutes or the current `rust_toolchain`
114
115 Args:
116 attr (struct): The current rule's attributes
117 toolchain (rust_toolchain): The `rust_toolchain` for the current target
Brian Silverman5f6f2762022-08-13 19:30:05 -0700118 label (Label): The label of the target being built
Brian Silvermancc09f182022-03-09 15:40:20 -0800119
120 Returns:
121 str: The target Rust edition
122 """
123 if getattr(attr, "edition"):
124 return attr.edition
Brian Silverman5f6f2762022-08-13 19:30:05 -0700125 elif not toolchain.default_edition:
126 fail("Attribute `edition` is required for {}.".format(label))
Brian Silvermancc09f182022-03-09 15:40:20 -0800127 else:
128 return toolchain.default_edition
129
Brian Silverman5f6f2762022-08-13 19:30:05 -0700130def _transform_sources(ctx, srcs, crate_root):
131 """Creates symlinks of the source files if needed.
132
133 Rustc assumes that the source files are located next to the crate root.
134 In case of a mix between generated and non-generated source files, this
135 we violate this assumption, as part of the sources will be located under
136 bazel-out/... . In order to allow for targets that contain both generated
137 and non-generated source files, we generate symlinks for all non-generated
138 files.
Brian Silvermancc09f182022-03-09 15:40:20 -0800139
140 Args:
Brian Silverman5f6f2762022-08-13 19:30:05 -0700141 ctx (struct): The current rule's context.
142 srcs (List[File]): The sources listed in the `srcs` attribute
143 crate_root (File): The file specified in the `crate_root` attribute,
144 if it exists, otherwise None
145
146 Returns:
147 Tuple(List[File], File): The transformed srcs and crate_root
148 """
149 has_generated_sources = len([src for src in srcs if not src.is_source]) > 0
150
151 if not has_generated_sources:
152 return srcs, crate_root
153
154 generated_sources = []
155
156 generated_root = crate_root
Adam Snaider1c095c92023-07-08 02:09:58 -0400157 package_root = paths.dirname(ctx.build_file_path)
Brian Silverman5f6f2762022-08-13 19:30:05 -0700158
159 if crate_root and (crate_root.is_source or crate_root.root.path != ctx.bin_dir.path):
Adam Snaider1c095c92023-07-08 02:09:58 -0400160 generated_root = ctx.actions.declare_file(paths.relativize(crate_root.short_path, package_root))
Brian Silverman5f6f2762022-08-13 19:30:05 -0700161 ctx.actions.symlink(
162 output = generated_root,
163 target_file = crate_root,
164 progress_message = "Creating symlink to source file: {}".format(crate_root.path),
165 )
166 if generated_root:
167 generated_sources.append(generated_root)
168
169 for src in srcs:
170 # We took care of the crate root above.
171 if src == crate_root:
172 continue
173 if src.is_source or src.root.path != ctx.bin_dir.path:
Adam Snaider1c095c92023-07-08 02:09:58 -0400174 src_symlink = ctx.actions.declare_file(paths.relativize(src.short_path, package_root))
Brian Silverman5f6f2762022-08-13 19:30:05 -0700175 ctx.actions.symlink(
176 output = src_symlink,
177 target_file = src,
178 progress_message = "Creating symlink to source file: {}".format(src.path),
179 )
180 generated_sources.append(src_symlink)
181 else:
182 generated_sources.append(src)
183
184 return generated_sources, generated_root
185
Brian Silvermancc09f182022-03-09 15:40:20 -0800186def _rust_library_impl(ctx):
187 """The implementation of the `rust_library` rule.
188
189 This rule provides CcInfo, so it can be used everywhere Bazel
190 expects rules_cc, but care must be taken to have the correct
191 dependencies on an allocator and std implemetation as needed.
192
193 Args:
194 ctx (ctx): The rule's context object
195
196 Returns:
197 list: A list of providers.
198 """
199 return _rust_library_common(ctx, "rlib")
200
201def _rust_static_library_impl(ctx):
202 """The implementation of the `rust_static_library` rule.
203
204 This rule provides CcInfo, so it can be used everywhere Bazel
205 expects rules_cc.
206
207 Args:
208 ctx (ctx): The rule's context object
209
210 Returns:
211 list: A list of providers.
212 """
213 return _rust_library_common(ctx, "staticlib")
214
215def _rust_shared_library_impl(ctx):
216 """The implementation of the `rust_shared_library` rule.
217
218 This rule provides CcInfo, so it can be used everywhere Bazel
219 expects rules_cc.
220
221 On Windows, a PDB file containing debugging information is available under
222 the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder
223 is available under the key `dsym_folder` in `OutputGroupInfo`.
224
225 Args:
226 ctx (ctx): The rule's context object
227
228 Returns:
229 list: A list of providers.
230 """
231 return _rust_library_common(ctx, "cdylib")
232
233def _rust_proc_macro_impl(ctx):
234 """The implementation of the `rust_proc_macro` rule.
235
236 Args:
237 ctx (ctx): The rule's context object
238
239 Returns:
240 list: A list of providers.
241 """
242 return _rust_library_common(ctx, "proc-macro")
243
244def _rust_library_common(ctx, crate_type):
245 """The common implementation of the library-like rules.
246
247 Args:
248 ctx (ctx): The rule's context object
249 crate_type (String): one of lib|rlib|dylib|staticlib|cdylib|proc-macro
250
251 Returns:
252 list: A list of providers. See `rustc_compile_action`
253 """
Brian Silvermancc09f182022-03-09 15:40:20 -0800254 _assert_no_deprecated_attributes(ctx)
255 _assert_correct_dep_mapping(ctx)
256
257 toolchain = find_toolchain(ctx)
258
Adam Snaider1c095c92023-07-08 02:09:58 -0400259 crate_root = getattr(ctx.file, "crate_root", None)
260 if not crate_root:
261 crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_type)
262 srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, crate_root)
263
Brian Silvermancc09f182022-03-09 15:40:20 -0800264 # Determine unique hash for this rlib.
Brian Silverman5f6f2762022-08-13 19:30:05 -0700265 # Note that we don't include a hash for `cdylib` and `staticlib` since they are meant to be consumed externally
266 # and having a deterministic name is important since it ends up embedded in the executable. This is problematic
267 # when one needs to include the library with a specific filename into a larger application.
Brian Silvermancc09f182022-03-09 15:40:20 -0800268 # (see https://github.com/bazelbuild/rules_rust/issues/405#issuecomment-993089889 for more details)
Brian Silverman5f6f2762022-08-13 19:30:05 -0700269 if crate_type in ["cdylib", "staticlib"]:
Brian Silvermancc09f182022-03-09 15:40:20 -0800270 output_hash = None
Brian Silverman5f6f2762022-08-13 19:30:05 -0700271 else:
272 output_hash = determine_output_hash(crate_root, ctx.label)
Brian Silvermancc09f182022-03-09 15:40:20 -0800273
274 crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
275 rust_lib_name = _determine_lib_name(
276 crate_name,
277 crate_type,
278 toolchain,
279 output_hash,
280 )
281 rust_lib = ctx.actions.declare_file(rust_lib_name)
282
Brian Silverman5f6f2762022-08-13 19:30:05 -0700283 rust_metadata = None
284 if can_build_metadata(toolchain, ctx, crate_type) and not ctx.attr.disable_pipelining:
285 rust_metadata = ctx.actions.declare_file(
286 paths.replace_extension(rust_lib_name, ".rmeta"),
287 sibling = rust_lib,
288 )
289
Brian Silvermancc09f182022-03-09 15:40:20 -0800290 deps = transform_deps(ctx.attr.deps)
291 proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
292
293 return rustc_compile_action(
294 ctx = ctx,
295 attr = ctx.attr,
296 toolchain = toolchain,
297 crate_info = rust_common.create_crate_info(
298 name = crate_name,
299 type = crate_type,
300 root = crate_root,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700301 srcs = depset(srcs),
Brian Silvermancc09f182022-03-09 15:40:20 -0800302 deps = depset(deps),
303 proc_macro_deps = depset(proc_macro_deps),
304 aliases = ctx.attr.aliases,
305 output = rust_lib,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700306 metadata = rust_metadata,
307 edition = get_edition(ctx.attr, toolchain, ctx.label),
Brian Silvermancc09f182022-03-09 15:40:20 -0800308 rustc_env = ctx.attr.rustc_env,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700309 rustc_env_files = ctx.files.rustc_env_files,
Brian Silvermancc09f182022-03-09 15:40:20 -0800310 is_test = False,
311 compile_data = depset(ctx.files.compile_data),
Adam Snaider1c095c92023-07-08 02:09:58 -0400312 compile_data_targets = depset(ctx.attr.compile_data),
Brian Silvermancc09f182022-03-09 15:40:20 -0800313 owner = ctx.label,
314 ),
315 output_hash = output_hash,
316 )
317
318def _rust_binary_impl(ctx):
319 """The implementation of the `rust_binary` rule
320
321 Args:
322 ctx (ctx): The rule's context object
323
324 Returns:
325 list: A list of providers. See `rustc_compile_action`
326 """
327 toolchain = find_toolchain(ctx)
328 crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
329 _assert_correct_dep_mapping(ctx)
330
331 output = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext)
332
333 deps = transform_deps(ctx.attr.deps)
334 proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
335
Adam Snaider1c095c92023-07-08 02:09:58 -0400336 crate_root = getattr(ctx.file, "crate_root", None)
Brian Silverman5f6f2762022-08-13 19:30:05 -0700337 if not crate_root:
Adam Snaider1c095c92023-07-08 02:09:58 -0400338 crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, ctx.attr.crate_type)
339 srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, crate_root)
Brian Silverman5f6f2762022-08-13 19:30:05 -0700340
Brian Silvermancc09f182022-03-09 15:40:20 -0800341 return rustc_compile_action(
342 ctx = ctx,
343 attr = ctx.attr,
344 toolchain = toolchain,
345 crate_info = rust_common.create_crate_info(
346 name = crate_name,
347 type = ctx.attr.crate_type,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700348 root = crate_root,
349 srcs = depset(srcs),
Brian Silvermancc09f182022-03-09 15:40:20 -0800350 deps = depset(deps),
351 proc_macro_deps = depset(proc_macro_deps),
352 aliases = ctx.attr.aliases,
353 output = output,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700354 edition = get_edition(ctx.attr, toolchain, ctx.label),
Brian Silvermancc09f182022-03-09 15:40:20 -0800355 rustc_env = ctx.attr.rustc_env,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700356 rustc_env_files = ctx.files.rustc_env_files,
Brian Silvermancc09f182022-03-09 15:40:20 -0800357 is_test = False,
358 compile_data = depset(ctx.files.compile_data),
Adam Snaider1c095c92023-07-08 02:09:58 -0400359 compile_data_targets = depset(ctx.attr.compile_data),
Brian Silvermancc09f182022-03-09 15:40:20 -0800360 owner = ctx.label,
361 ),
362 )
363
Brian Silverman5f6f2762022-08-13 19:30:05 -0700364def _rust_test_impl(ctx):
365 """The implementation of the `rust_test` rule.
Brian Silvermancc09f182022-03-09 15:40:20 -0800366
367 Args:
368 ctx (ctx): The ctx object for the current target.
Brian Silvermancc09f182022-03-09 15:40:20 -0800369
370 Returns:
371 list: The list of providers. See `rustc_compile_action`
372 """
373 _assert_no_deprecated_attributes(ctx)
374 _assert_correct_dep_mapping(ctx)
375
Brian Silverman5f6f2762022-08-13 19:30:05 -0700376 toolchain = find_toolchain(ctx)
377
Brian Silvermancc09f182022-03-09 15:40:20 -0800378 crate_type = "bin"
Brian Silvermancc09f182022-03-09 15:40:20 -0800379 deps = transform_deps(ctx.attr.deps)
380 proc_macro_deps = transform_deps(ctx.attr.proc_macro_deps + get_import_macro_deps(ctx))
381
382 if ctx.attr.crate:
383 # Target is building the crate in `test` config
Brian Silverman5f6f2762022-08-13 19:30:05 -0700384 crate = ctx.attr.crate[rust_common.crate_info] if rust_common.crate_info in ctx.attr.crate else ctx.attr.crate[rust_common.test_crate_info].crate
385
386 output_hash = determine_output_hash(crate.root, ctx.label)
387 output = ctx.actions.declare_file(
388 "test-%s/%s%s" % (
389 output_hash,
390 ctx.label.name,
391 toolchain.binary_ext,
392 ),
393 )
Brian Silvermancc09f182022-03-09 15:40:20 -0800394
Adam Snaider1c095c92023-07-08 02:09:58 -0400395 srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, getattr(ctx.file, "crate_root", None))
396
Brian Silvermancc09f182022-03-09 15:40:20 -0800397 # Optionally join compile data
398 if crate.compile_data:
399 compile_data = depset(ctx.files.compile_data, transitive = [crate.compile_data])
400 else:
401 compile_data = depset(ctx.files.compile_data)
Adam Snaider1c095c92023-07-08 02:09:58 -0400402 if crate.compile_data_targets:
403 compile_data_targets = depset(ctx.attr.compile_data, transitive = [crate.compile_data_targets])
404 else:
405 compile_data_targets = depset(ctx.attr.compile_data)
Brian Silverman5f6f2762022-08-13 19:30:05 -0700406 rustc_env_files = ctx.files.rustc_env_files + crate.rustc_env_files
407 rustc_env = dict(crate.rustc_env)
408 rustc_env.update(**ctx.attr.rustc_env)
Brian Silvermancc09f182022-03-09 15:40:20 -0800409
410 # Build the test binary using the dependency's srcs.
411 crate_info = rust_common.create_crate_info(
Brian Silverman5f6f2762022-08-13 19:30:05 -0700412 name = crate.name,
Brian Silvermancc09f182022-03-09 15:40:20 -0800413 type = crate_type,
414 root = crate.root,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700415 srcs = depset(srcs, transitive = [crate.srcs]),
Brian Silvermancc09f182022-03-09 15:40:20 -0800416 deps = depset(deps, transitive = [crate.deps]),
417 proc_macro_deps = depset(proc_macro_deps, transitive = [crate.proc_macro_deps]),
418 aliases = ctx.attr.aliases,
419 output = output,
420 edition = crate.edition,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700421 rustc_env = rustc_env,
422 rustc_env_files = rustc_env_files,
Brian Silvermancc09f182022-03-09 15:40:20 -0800423 is_test = True,
424 compile_data = compile_data,
Adam Snaider1c095c92023-07-08 02:09:58 -0400425 compile_data_targets = compile_data_targets,
Brian Silvermancc09f182022-03-09 15:40:20 -0800426 wrapped_crate_type = crate.type,
427 owner = ctx.label,
428 )
429 else:
Adam Snaider1c095c92023-07-08 02:09:58 -0400430 crate_root = getattr(ctx.file, "crate_root", None)
431
Brian Silverman5f6f2762022-08-13 19:30:05 -0700432 if not crate_root:
Adam Snaider1c095c92023-07-08 02:09:58 -0400433 crate_root_type = "lib" if ctx.attr.use_libtest_harness else "bin"
434 crate_root = crate_root_src(ctx.attr.name, ctx.files.srcs, crate_root_type)
435 srcs, crate_root = _transform_sources(ctx, ctx.files.srcs, crate_root)
Brian Silverman5f6f2762022-08-13 19:30:05 -0700436
437 output_hash = determine_output_hash(crate_root, ctx.label)
438 output = ctx.actions.declare_file(
439 "test-%s/%s%s" % (
440 output_hash,
441 ctx.label.name,
442 toolchain.binary_ext,
443 ),
444 )
445
Brian Silvermancc09f182022-03-09 15:40:20 -0800446 # Target is a standalone crate. Build the test binary as its own crate.
447 crate_info = rust_common.create_crate_info(
Brian Silverman5f6f2762022-08-13 19:30:05 -0700448 name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name),
Brian Silvermancc09f182022-03-09 15:40:20 -0800449 type = crate_type,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700450 root = crate_root,
451 srcs = depset(srcs),
Brian Silvermancc09f182022-03-09 15:40:20 -0800452 deps = depset(deps),
453 proc_macro_deps = depset(proc_macro_deps),
454 aliases = ctx.attr.aliases,
455 output = output,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700456 edition = get_edition(ctx.attr, toolchain, ctx.label),
Brian Silvermancc09f182022-03-09 15:40:20 -0800457 rustc_env = ctx.attr.rustc_env,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700458 rustc_env_files = ctx.files.rustc_env_files,
Brian Silvermancc09f182022-03-09 15:40:20 -0800459 is_test = True,
460 compile_data = depset(ctx.files.compile_data),
Adam Snaider1c095c92023-07-08 02:09:58 -0400461 compile_data_targets = depset(ctx.attr.compile_data),
Brian Silvermancc09f182022-03-09 15:40:20 -0800462 owner = ctx.label,
463 )
464
465 providers = rustc_compile_action(
466 ctx = ctx,
467 attr = ctx.attr,
468 toolchain = toolchain,
469 crate_info = crate_info,
470 rust_flags = ["--test"] if ctx.attr.use_libtest_harness else ["--cfg", "test"],
471 )
472 data = getattr(ctx.attr, "data", [])
473
474 env = expand_dict_value_locations(
475 ctx,
476 getattr(ctx.attr, "env", {}),
477 data,
478 )
Brian Silverman5f6f2762022-08-13 19:30:05 -0700479 if toolchain.llvm_cov and ctx.configuration.coverage_enabled:
480 if not toolchain.llvm_profdata:
481 fail("toolchain.llvm_profdata is required if toolchain.llvm_cov is set.")
482
Adam Snaider1c095c92023-07-08 02:09:58 -0400483 llvm_cov_path = toolchain.llvm_cov.short_path
484 if llvm_cov_path.startswith("../"):
485 llvm_cov_path = llvm_cov_path[len("../"):]
486
487 llvm_profdata_path = toolchain.llvm_profdata.short_path
488 if llvm_profdata_path.startswith("../"):
489 llvm_profdata_path = llvm_profdata_path[len("../"):]
490
491 env["RUST_LLVM_COV"] = llvm_cov_path
492 env["RUST_LLVM_PROFDATA"] = llvm_profdata_path
493 components = "{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
494 env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
Brian Silvermancc09f182022-03-09 15:40:20 -0800495 providers.append(testing.TestEnvironment(env))
496
497 return providers
498
Adam Snaider1c095c92023-07-08 02:09:58 -0400499def _rust_library_group_impl(ctx):
500 dep_variant_infos = []
501 dep_variant_transitive_infos = []
502 runfiles = []
503
504 for dep in ctx.attr.deps:
505 if rust_common.crate_info in dep:
506 dep_variant_infos.append(rust_common.dep_variant_info(
507 crate_info = dep[rust_common.crate_info] if rust_common.crate_info in dep else None,
508 dep_info = dep[rust_common.dep_info] if rust_common.crate_info in dep else None,
509 build_info = dep[BuildInfo] if BuildInfo in dep else None,
510 cc_info = dep[CcInfo] if CcInfo in dep else None,
511 crate_group_info = None,
512 ))
513 elif rust_common.crate_group_info in dep:
514 dep_variant_transitive_infos.append(dep[rust_common.crate_group_info].dep_variant_infos)
515 else:
516 fail("crate_group_info targets can only depend on rust_library or rust_library_group targets.")
517
518 if dep[DefaultInfo].default_runfiles != None:
519 runfiles.append(dep[DefaultInfo].default_runfiles)
520
521 return [
522 rust_common.crate_group_info(
523 dep_variant_infos = depset(dep_variant_infos, transitive = dep_variant_transitive_infos),
524 ),
525 DefaultInfo(runfiles = ctx.runfiles().merge_all(runfiles)),
526 coverage_common.instrumented_files_info(
527 ctx,
528 dependency_attributes = ["deps"],
529 ),
530 ]
531
Brian Silverman5f6f2762022-08-13 19:30:05 -0700532def _stamp_attribute(default_value):
533 return attr.int(
534 doc = dedent("""\
535 Whether to encode build information into the `Rustc` action. Possible values:
Brian Silvermancc09f182022-03-09 15:40:20 -0800536
Brian Silverman5f6f2762022-08-13 19:30:05 -0700537 - `stamp = 1`: Always stamp the build information into the `Rustc` action, even in \
538 [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \
539 This setting should be avoided, since it potentially kills remote caching for the target and \
540 any downstream actions that depend on it.
Brian Silvermancc09f182022-03-09 15:40:20 -0800541
Brian Silverman5f6f2762022-08-13 19:30:05 -0700542 - `stamp = 0`: Always replace build information by constant values. This gives good build result caching.
Brian Silvermancc09f182022-03-09 15:40:20 -0800543
Brian Silverman5f6f2762022-08-13 19:30:05 -0700544 - `stamp = -1`: Embedding of build information is controlled by the \
545 [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
546
547 Stamped targets are not rebuilt unless their dependencies change.
548
549 For example if a `rust_library` is stamped, and a `rust_binary` depends on that library, the stamped
550 library won't be rebuilt when we change sources of the `rust_binary`. This is different from how
551 [`cc_library.linkstamps`](https://docs.bazel.build/versions/main/be/c-cpp.html#cc_library.linkstamp)
552 behaves.
553 """),
554 default = default_value,
555 values = [1, 0, -1],
Brian Silvermancc09f182022-03-09 15:40:20 -0800556 )
557
Brian Silvermancc09f182022-03-09 15:40:20 -0800558_common_attrs = {
559 "aliases": attr.label_keyed_string_dict(
560 doc = dedent("""\
561 Remap crates to a new name or moniker for linkage to this target
562
563 These are other `rust_library` targets and will be presented as the new name given.
564 """),
565 ),
566 "compile_data": attr.label_list(
567 doc = dedent("""\
568 List of files used by this rule at compile time.
569
570 This attribute can be used to specify any data files that are embedded into
571 the library, such as via the
572 [`include_str!`](https://doc.rust-lang.org/std/macro.include_str!.html)
573 macro.
574 """),
575 allow_files = True,
576 ),
577 "crate_features": attr.string_list(
578 doc = dedent("""\
579 List of features to enable for this crate.
580
581 Features are defined in the code using the `#[cfg(feature = "foo")]`
582 configuration option. The features listed here will be passed to `rustc`
583 with `--cfg feature="${feature_name}"` flags.
584 """),
585 ),
586 "crate_name": attr.string(
587 doc = dedent("""\
588 Crate name to use for this target.
589
590 This must be a valid Rust identifier, i.e. it may contain only alphanumeric characters and underscores.
591 Defaults to the target name, with any hyphens replaced by underscores.
592 """),
593 ),
594 "crate_root": attr.label(
595 doc = dedent("""\
596 The file that will be passed to `rustc` to be used for building this crate.
597
598 If `crate_root` is not set, then this rule will look for a `lib.rs` file (or `main.rs` for rust_binary)
599 or the single file in `srcs` if `srcs` contains only one file.
600 """),
601 allow_single_file = [".rs"],
602 ),
603 "data": attr.label_list(
604 doc = dedent("""\
605 List of files used by this rule at compile time and runtime.
606
607 If including data at compile time with include_str!() and similar,
608 prefer `compile_data` over `data`, to prevent the data also being included
609 in the runfiles.
610 """),
611 allow_files = True,
612 ),
613 "deps": attr.label_list(
614 doc = dedent("""\
615 List of other libraries to be linked to this library target.
616
617 These can be either other `rust_library` targets or `cc_library` targets if
618 linking a native library.
619 """),
620 ),
621 "edition": attr.string(
622 doc = "The rust edition to use for this crate. Defaults to the edition specified in the rust_toolchain.",
623 ),
624 # Previously `proc_macro_deps` were a part of `deps`, and then proc_macro_host_transition was
625 # used into cfg="host" using `@local_config_platform//:host`.
626 # This fails for remote execution, which needs cfg="exec", and there isn't anything like
627 # `@local_config_platform//:exec` exposed.
628 "proc_macro_deps": attr.label_list(
629 doc = dedent("""\
630 List of `rust_library` targets with kind `proc-macro` used to help build this library target.
631 """),
632 cfg = "exec",
633 providers = [rust_common.crate_info],
634 ),
635 "rustc_env": attr.string_dict(
636 doc = dedent("""\
637 Dictionary of additional `"key": "value"` environment variables to set for rustc.
638
639 rust_test()/rust_binary() rules can use $(rootpath //package:target) to pass in the
640 location of a generated file or external tool. Cargo build scripts that wish to
641 expand locations should use cargo_build_script()'s build_script_env argument instead,
642 as build scripts are run in a different environment - see cargo_build_script()'s
643 documentation for more.
644 """),
645 ),
646 "rustc_env_files": attr.label_list(
647 doc = dedent("""\
648 Files containing additional environment variables to set for rustc.
649
650 These files should contain a single variable per line, of format
651 `NAME=value`, and newlines may be included in a value by ending a
652 line with a trailing back-slash (`\\\\`).
653
654 The order that these files will be processed is unspecified, so
655 multiple definitions of a particular variable are discouraged.
656
657 Note that the variables here are subject to
658 [workspace status](https://docs.bazel.build/versions/main/user-manual.html#workspace_status)
659 stamping should the `stamp` attribute be enabled. Stamp variables
660 should be wrapped in brackets in order to be resolved. E.g.
661 `NAME={WORKSPACE_STATUS_VARIABLE}`.
662 """),
663 allow_files = True,
664 ),
665 "rustc_flags": attr.string_list(
666 doc = dedent("""\
667 List of compiler flags passed to `rustc`.
668
669 These strings are subject to Make variable expansion for predefined
670 source/output path variables like `$location`, `$execpath`, and
671 `$rootpath`. This expansion is useful if you wish to pass a generated
672 file of arguments to rustc: `@$(location //package:target)`.
673 """),
674 ),
675 # TODO(stardoc): How do we provide additional documentation to an inherited attribute?
676 # "name": attr.string(
677 # doc = "This name will also be used as the name of the crate built by this rule.",
678 # `),
679 "srcs": attr.label_list(
680 doc = dedent("""\
681 List of Rust `.rs` source files used to build the library.
682
683 If `srcs` contains more than one file, then there must be a file either
684 named `lib.rs`. Otherwise, `crate_root` must be set to the source file that
685 is the root of the crate to be passed to rustc to build this crate.
686 """),
687 allow_files = [".rs"],
688 ),
Brian Silverman5f6f2762022-08-13 19:30:05 -0700689 "stamp": _stamp_attribute(default_value = 0),
Brian Silvermancc09f182022-03-09 15:40:20 -0800690 "version": attr.string(
691 doc = "A version to inject in the cargo environment variable.",
692 default = "0.0.0",
693 ),
694 "_cc_toolchain": attr.label(
Brian Silverman5f6f2762022-08-13 19:30:05 -0700695 doc = (
696 "In order to use find_cc_toolchain, your rule has to depend " +
697 "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " +
698 "docs for details."
699 ),
700 default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
Brian Silvermancc09f182022-03-09 15:40:20 -0800701 ),
Brian Silverman5f6f2762022-08-13 19:30:05 -0700702 "_error_format": attr.label(
703 default = Label("//:error_format"),
704 ),
705 "_extra_exec_rustc_flag": attr.label(
706 default = Label("//:extra_exec_rustc_flag"),
707 ),
708 "_extra_exec_rustc_flags": attr.label(
709 default = Label("//:extra_exec_rustc_flags"),
710 ),
711 "_extra_rustc_flag": attr.label(
712 default = Label("//:extra_rustc_flag"),
713 ),
714 "_extra_rustc_flags": attr.label(
715 default = Label("//:extra_rustc_flags"),
716 ),
Brian Silvermancc09f182022-03-09 15:40:20 -0800717 "_import_macro_dep": attr.label(
Brian Silverman5f6f2762022-08-13 19:30:05 -0700718 default = Label("//util/import"),
719 cfg = "exec",
720 ),
721 "_is_proc_macro_dep": attr.label(
722 default = Label("//:is_proc_macro_dep"),
723 ),
724 "_is_proc_macro_dep_enabled": attr.label(
725 default = Label("//:is_proc_macro_dep_enabled"),
Brian Silvermancc09f182022-03-09 15:40:20 -0800726 ),
Adam Snaider1c095c92023-07-08 02:09:58 -0400727 "_per_crate_rustc_flag": attr.label(
728 default = Label("//:experimental_per_crate_rustc_flag"),
729 ),
Brian Silvermancc09f182022-03-09 15:40:20 -0800730 "_process_wrapper": attr.label(
Brian Silverman5f6f2762022-08-13 19:30:05 -0700731 doc = "A process wrapper for running rustc on all platforms.",
Brian Silvermancc09f182022-03-09 15:40:20 -0800732 default = Label("//util/process_wrapper"),
733 executable = True,
734 allow_single_file = True,
735 cfg = "exec",
736 ),
737 "_stamp_flag": attr.label(
738 doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
739 default = Label("//rust/private:stamp"),
740 ),
741}
742
Adam Snaider1c095c92023-07-08 02:09:58 -0400743_coverage_attrs = {
744 "_collect_cc_coverage": attr.label(
745 default = Label("//util:collect_coverage"),
746 executable = True,
747 cfg = "exec",
748 ),
749 # Bazel’s coverage runner
750 # (https://github.com/bazelbuild/bazel/blob/6.0.0/tools/test/collect_coverage.sh)
751 # needs a binary called “lcov_merge.” Its location is passed in the
752 # LCOV_MERGER environmental variable. For builtin rules, this variable
753 # is set automatically based on a magic “$lcov_merger” or
754 # “:lcov_merger” attribute, but it’s not possible to create such
755 # attributes in Starlark. Therefore we specify the variable ourselves.
756 # Note that the coverage runner runs in the runfiles root instead of
757 # the execution root, therefore we use “path” instead of “short_path.”
758 "_lcov_merger": attr.label(
759 default = configuration_field(fragment = "coverage", name = "output_generator"),
760 executable = True,
761 cfg = "exec",
762 ),
763}
764
Brian Silverman5f6f2762022-08-13 19:30:05 -0700765_experimental_use_cc_common_link_attrs = {
766 "experimental_use_cc_common_link": attr.int(
767 doc = (
768 "Whether to use cc_common.link to link rust binaries. " +
769 "Possible values: [-1, 0, 1]. " +
770 "-1 means use the value of the toolchain.experimental_use_cc_common_link " +
771 "boolean build setting to determine. " +
772 "0 means do not use cc_common.link (use rustc instead). " +
773 "1 means use cc_common.link."
774 ),
775 values = [-1, 0, 1],
776 default = -1,
777 ),
Adam Snaider1c095c92023-07-08 02:09:58 -0400778 "malloc": attr.label(
779 default = Label("@bazel_tools//tools/cpp:malloc"),
780 doc = """Override the default dependency on `malloc`.
781
782By default, Rust binaries linked with cc_common.link are linked against
783`@bazel_tools//tools/cpp:malloc"`, which is an empty library and the resulting binary will use
784libc's `malloc`. This label must refer to a `cc_library` rule.
785""",
786 mandatory = False,
787 providers = [[CcInfo]],
788 ), # A late-bound attribute denoting the value of the `--custom_malloc`
789 # command line flag (or None if the flag is not provided).
790 "_custom_malloc": attr.label(
791 default = configuration_field(
792 fragment = "cpp",
793 name = "custom_malloc",
794 ),
795 providers = [[CcInfo]],
796 ),
Brian Silverman5f6f2762022-08-13 19:30:05 -0700797}
798
799_rust_test_attrs = dict({
Brian Silvermancc09f182022-03-09 15:40:20 -0800800 "crate": attr.label(
801 mandatory = False,
802 doc = dedent("""\
803 Target inline tests declared in the given crate
804
805 These tests are typically those that would be held out under
806 `#[cfg(test)]` declarations.
807 """),
808 ),
809 "env": attr.string_dict(
810 mandatory = False,
811 doc = dedent("""\
812 Specifies additional environment variables to set when the test is executed by bazel test.
813 Values are subject to `$(rootpath)`, `$(execpath)`, location, and
814 ["Make variable"](https://docs.bazel.build/versions/master/be/make-variables.html) substitution.
Brian Silvermancc09f182022-03-09 15:40:20 -0800815 """),
816 ),
817 "use_libtest_harness": attr.bool(
818 mandatory = False,
819 default = True,
820 doc = dedent("""\
821 Whether to use `libtest`. For targets using this flag, individual tests can be run by using the
822 [--test_arg](https://docs.bazel.build/versions/4.0.0/command-line-reference.html#flag--test_arg) flag.
823 E.g. `bazel test //src:rust_test --test_arg=foo::test::test_fn`.
824 """),
825 ),
826 "_grep_includes": attr.label(
827 allow_single_file = True,
828 cfg = "exec",
829 default = Label("@bazel_tools//tools/cpp:grep-includes"),
830 executable = True,
831 ),
Adam Snaider1c095c92023-07-08 02:09:58 -0400832}.items() + _coverage_attrs.items() + _experimental_use_cc_common_link_attrs.items())
Brian Silvermancc09f182022-03-09 15:40:20 -0800833
834_common_providers = [
835 rust_common.crate_info,
836 rust_common.dep_info,
837 DefaultInfo,
838]
839
840rust_library = rule(
841 implementation = _rust_library_impl,
842 provides = _common_providers,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700843 attrs = dict(_common_attrs.items() + {
844 "disable_pipelining": attr.bool(
845 default = False,
846 doc = dedent("""\
847 Disables pipelining for this rule if it is globally enabled.
848 This will cause this rule to not produce a `.rmeta` file and all the dependent
849 crates will instead use the `.rlib` file.
850 """),
851 ),
852 }.items()),
Brian Silvermancc09f182022-03-09 15:40:20 -0800853 fragments = ["cpp"],
854 host_fragments = ["cpp"],
855 toolchains = [
Brian Silverman5f6f2762022-08-13 19:30:05 -0700856 str(Label("//rust:toolchain_type")),
Brian Silvermancc09f182022-03-09 15:40:20 -0800857 "@bazel_tools//tools/cpp:toolchain_type",
858 ],
859 incompatible_use_toolchain_transition = True,
860 doc = dedent("""\
861 Builds a Rust library crate.
862
863 Example:
864
865 Suppose you have the following directory structure for a simple Rust library crate:
866
867 ```output
868 [workspace]/
869 WORKSPACE
870 hello_lib/
871 BUILD
872 src/
873 greeter.rs
874 lib.rs
875 ```
876
877 `hello_lib/src/greeter.rs`:
878 ```rust
879 pub struct Greeter {
880 greeting: String,
881 }
882
883 impl Greeter {
884 pub fn new(greeting: &str) -> Greeter {
885 Greeter { greeting: greeting.to_string(), }
886 }
887
888 pub fn greet(&self, thing: &str) {
889 println!("{} {}", &self.greeting, thing);
890 }
891 }
892 ```
893
894 `hello_lib/src/lib.rs`:
895
896 ```rust
897 pub mod greeter;
898 ```
899
900 `hello_lib/BUILD`:
901 ```python
902 package(default_visibility = ["//visibility:public"])
903
904 load("@rules_rust//rust:defs.bzl", "rust_library")
905
906 rust_library(
907 name = "hello_lib",
908 srcs = [
909 "src/greeter.rs",
910 "src/lib.rs",
911 ],
912 )
913 ```
914
915 Build the library:
916 ```output
917 $ bazel build //hello_lib
918 INFO: Found 1 target...
919 Target //examples/rust/hello_lib:hello_lib up-to-date:
920 bazel-bin/examples/rust/hello_lib/libhello_lib.rlib
921 INFO: Elapsed time: 1.245s, Critical Path: 1.01s
922 ```
923 """),
924)
925
926rust_static_library = rule(
927 implementation = _rust_static_library_impl,
Brian Silvermancc09f182022-03-09 15:40:20 -0800928 attrs = dict(_common_attrs.items()),
929 fragments = ["cpp"],
930 host_fragments = ["cpp"],
931 toolchains = [
Brian Silverman5f6f2762022-08-13 19:30:05 -0700932 str(Label("//rust:toolchain_type")),
Brian Silvermancc09f182022-03-09 15:40:20 -0800933 "@bazel_tools//tools/cpp:toolchain_type",
934 ],
935 incompatible_use_toolchain_transition = True,
936 doc = dedent("""\
937 Builds a Rust static library.
938
939 This static library will contain all transitively reachable crates and native objects.
940 It is meant to be used when producing an artifact that is then consumed by some other build system
941 (for example to produce an archive that Python program links against).
942
943 This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`.
944
945 When building the whole binary in Bazel, use `rust_library` instead.
946 """),
947)
948
949rust_shared_library = rule(
950 implementation = _rust_shared_library_impl,
Adam Snaider1c095c92023-07-08 02:09:58 -0400951 attrs = dict(
952 _common_attrs.items() + _experimental_use_cc_common_link_attrs.items() + {
953 "_grep_includes": attr.label(
954 allow_single_file = True,
955 cfg = "exec",
956 default = Label("@bazel_tools//tools/cpp:grep-includes"),
957 executable = True,
958 ),
959 }.items(),
960 ),
Brian Silvermancc09f182022-03-09 15:40:20 -0800961 fragments = ["cpp"],
962 host_fragments = ["cpp"],
963 toolchains = [
Brian Silverman5f6f2762022-08-13 19:30:05 -0700964 str(Label("//rust:toolchain_type")),
Brian Silvermancc09f182022-03-09 15:40:20 -0800965 "@bazel_tools//tools/cpp:toolchain_type",
966 ],
967 incompatible_use_toolchain_transition = True,
968 doc = dedent("""\
969 Builds a Rust shared library.
970
971 This shared library will contain all transitively reachable crates and native objects.
972 It is meant to be used when producing an artifact that is then consumed by some other build system
973 (for example to produce a shared library that Python program links against).
974
975 This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`.
976
977 When building the whole binary in Bazel, use `rust_library` instead.
978 """),
979)
980
Brian Silverman5f6f2762022-08-13 19:30:05 -0700981def _proc_macro_dep_transition_impl(settings, _attr):
982 if settings["//:is_proc_macro_dep_enabled"]:
983 return {"//:is_proc_macro_dep": True}
984 else:
985 return []
986
987_proc_macro_dep_transition = transition(
988 inputs = ["//:is_proc_macro_dep_enabled"],
989 outputs = ["//:is_proc_macro_dep"],
990 implementation = _proc_macro_dep_transition_impl,
991)
992
Brian Silvermancc09f182022-03-09 15:40:20 -0800993rust_proc_macro = rule(
994 implementation = _rust_proc_macro_impl,
995 provides = _common_providers,
Brian Silverman5f6f2762022-08-13 19:30:05 -0700996 # Start by copying the common attributes, then override the `deps` attribute
997 # to apply `_proc_macro_dep_transition`. To add this transition we additionally
998 # need to declare `_allowlist_function_transition`, see
999 # https://docs.bazel.build/versions/main/skylark/config.html#user-defined-transitions.
1000 attrs = dict(
1001 _common_attrs.items(),
1002 _allowlist_function_transition = attr.label(default = Label("//tools/allowlists/function_transition_allowlist")),
1003 deps = attr.label_list(
1004 doc = dedent("""\
1005 List of other libraries to be linked to this library target.
1006
1007 These can be either other `rust_library` targets or `cc_library` targets if
1008 linking a native library.
1009 """),
1010 cfg = _proc_macro_dep_transition,
1011 ),
1012 ),
Brian Silvermancc09f182022-03-09 15:40:20 -08001013 fragments = ["cpp"],
1014 host_fragments = ["cpp"],
1015 toolchains = [
Brian Silverman5f6f2762022-08-13 19:30:05 -07001016 str(Label("//rust:toolchain_type")),
Brian Silvermancc09f182022-03-09 15:40:20 -08001017 "@bazel_tools//tools/cpp:toolchain_type",
1018 ],
1019 incompatible_use_toolchain_transition = True,
1020 doc = dedent("""\
1021 Builds a Rust proc-macro crate.
1022 """),
1023)
1024
Brian Silverman5f6f2762022-08-13 19:30:05 -07001025_rust_binary_attrs = dict({
Brian Silvermancc09f182022-03-09 15:40:20 -08001026 "crate_type": attr.string(
1027 doc = dedent("""\
1028 Crate type that will be passed to `rustc` to be used for building this crate.
1029
1030 This option is a temporary workaround and should be used only when building
1031 for WebAssembly targets (//rust/platform:wasi and //rust/platform:wasm).
1032 """),
1033 default = "bin",
1034 ),
1035 "linker_script": attr.label(
1036 doc = dedent("""\
1037 Link script to forward into linker via rustc options.
1038 """),
1039 cfg = "exec",
1040 allow_single_file = True,
1041 ),
1042 "out_binary": attr.bool(
1043 doc = (
1044 "Force a target, regardless of it's `crate_type`, to always mark the " +
1045 "file as executable. This attribute is only used to support wasm targets but is " +
1046 "expected to be removed following a resolution to https://github.com/bazelbuild/rules_rust/issues/771."
1047 ),
1048 default = False,
1049 ),
Brian Silverman5f6f2762022-08-13 19:30:05 -07001050 "stamp": _stamp_attribute(default_value = -1),
Brian Silvermancc09f182022-03-09 15:40:20 -08001051 "_grep_includes": attr.label(
1052 allow_single_file = True,
1053 cfg = "exec",
1054 default = Label("@bazel_tools//tools/cpp:grep-includes"),
1055 executable = True,
1056 ),
Brian Silverman5f6f2762022-08-13 19:30:05 -07001057}.items() + _experimental_use_cc_common_link_attrs.items())
Brian Silvermancc09f182022-03-09 15:40:20 -08001058
1059rust_binary = rule(
1060 implementation = _rust_binary_impl,
1061 provides = _common_providers,
1062 attrs = dict(_common_attrs.items() + _rust_binary_attrs.items()),
1063 executable = True,
1064 fragments = ["cpp"],
1065 host_fragments = ["cpp"],
1066 toolchains = [
Brian Silverman5f6f2762022-08-13 19:30:05 -07001067 str(Label("//rust:toolchain_type")),
Brian Silvermancc09f182022-03-09 15:40:20 -08001068 "@bazel_tools//tools/cpp:toolchain_type",
1069 ],
1070 incompatible_use_toolchain_transition = True,
1071 doc = dedent("""\
1072 Builds a Rust binary crate.
1073
1074 Example:
1075
1076 Suppose you have the following directory structure for a Rust project with a
1077 library crate, `hello_lib`, and a binary crate, `hello_world` that uses the
1078 `hello_lib` library:
1079
1080 ```output
1081 [workspace]/
1082 WORKSPACE
1083 hello_lib/
1084 BUILD
1085 src/
1086 lib.rs
1087 hello_world/
1088 BUILD
1089 src/
1090 main.rs
1091 ```
1092
1093 `hello_lib/src/lib.rs`:
1094 ```rust
1095 pub struct Greeter {
1096 greeting: String,
1097 }
1098
1099 impl Greeter {
1100 pub fn new(greeting: &str) -> Greeter {
1101 Greeter { greeting: greeting.to_string(), }
1102 }
1103
1104 pub fn greet(&self, thing: &str) {
1105 println!("{} {}", &self.greeting, thing);
1106 }
1107 }
1108 ```
1109
1110 `hello_lib/BUILD`:
1111 ```python
1112 package(default_visibility = ["//visibility:public"])
1113
1114 load("@rules_rust//rust:defs.bzl", "rust_library")
1115
1116 rust_library(
1117 name = "hello_lib",
1118 srcs = ["src/lib.rs"],
1119 )
1120 ```
1121
1122 `hello_world/src/main.rs`:
1123 ```rust
1124 extern crate hello_lib;
1125
1126 fn main() {
1127 let hello = hello_lib::Greeter::new("Hello");
1128 hello.greet("world");
1129 }
1130 ```
1131
1132 `hello_world/BUILD`:
1133 ```python
1134 load("@rules_rust//rust:defs.bzl", "rust_binary")
1135
1136 rust_binary(
1137 name = "hello_world",
1138 srcs = ["src/main.rs"],
1139 deps = ["//hello_lib"],
1140 )
1141 ```
1142
1143 Build and run `hello_world`:
1144 ```
1145 $ bazel run //hello_world
1146 INFO: Found 1 target...
1147 Target //examples/rust/hello_world:hello_world up-to-date:
1148 bazel-bin/examples/rust/hello_world/hello_world
1149 INFO: Elapsed time: 1.308s, Critical Path: 1.22s
1150
1151 INFO: Running command line: bazel-bin/examples/rust/hello_world/hello_world
1152 Hello world
1153 ```
1154
1155 On Windows, a PDB file containing debugging information is available under
1156 the key `pdb_file` in `OutputGroupInfo`. Similarly on macOS, a dSYM folder
1157 is available under the key `dsym_folder` in `OutputGroupInfo`.
1158"""),
1159)
1160
Brian Silverman5f6f2762022-08-13 19:30:05 -07001161def _common_attrs_for_binary_without_process_wrapper(attrs):
1162 new_attr = dict(attrs)
1163
1164 # use a fake process wrapper
1165 new_attr["_process_wrapper"] = attr.label(
1166 default = None,
1167 executable = True,
1168 allow_single_file = True,
1169 cfg = "exec",
1170 )
1171
1172 # fix stamp = 0
1173 new_attr["stamp"] = attr.int(
1174 doc = dedent("""\
1175 Fix `stamp = 0` as stamping is not supported when building without process_wrapper:
1176 https://github.com/bazelbuild/rules_rust/blob/8df4517d370b0c543a01ba38b63e1d5a4104b035/rust/private/rustc.bzl#L955
1177 """),
1178 default = 0,
1179 values = [0],
1180 )
1181
1182 return new_attr
1183
1184# Provides an internal rust_{binary,library} to use that we can use to build the process
1185# wrapper, this breaks the dependency of rust_* on the process wrapper by
1186# setting it to None, which the functions in rustc detect and build accordingly.
1187rust_binary_without_process_wrapper = rule(
1188 implementation = _rust_binary_impl,
1189 provides = _common_providers,
1190 attrs = _common_attrs_for_binary_without_process_wrapper(_common_attrs.items() + _rust_binary_attrs.items()),
1191 executable = True,
1192 fragments = ["cpp"],
1193 host_fragments = ["cpp"],
1194 toolchains = [
1195 str(Label("//rust:toolchain_type")),
1196 "@bazel_tools//tools/cpp:toolchain_type",
1197 ],
1198 incompatible_use_toolchain_transition = True,
1199)
1200
1201rust_library_without_process_wrapper = rule(
1202 implementation = _rust_library_impl,
1203 provides = _common_providers,
1204 attrs = dict(_common_attrs_for_binary_without_process_wrapper(_common_attrs).items()),
1205 fragments = ["cpp"],
1206 host_fragments = ["cpp"],
1207 toolchains = [
1208 str(Label("//rust:toolchain_type")),
1209 "@bazel_tools//tools/cpp:toolchain_type",
1210 ],
1211 incompatible_use_toolchain_transition = True,
1212)
1213
Brian Silvermancc09f182022-03-09 15:40:20 -08001214rust_test = rule(
1215 implementation = _rust_test_impl,
1216 provides = _common_providers,
1217 attrs = dict(_common_attrs.items() +
1218 _rust_test_attrs.items()),
1219 executable = True,
1220 fragments = ["cpp"],
1221 host_fragments = ["cpp"],
1222 test = True,
1223 toolchains = [
Brian Silverman5f6f2762022-08-13 19:30:05 -07001224 str(Label("//rust:toolchain_type")),
Brian Silvermancc09f182022-03-09 15:40:20 -08001225 "@bazel_tools//tools/cpp:toolchain_type",
1226 ],
1227 incompatible_use_toolchain_transition = True,
1228 doc = dedent("""\
1229 Builds a Rust test crate.
1230
1231 Examples:
1232
1233 Suppose you have the following directory structure for a Rust library crate \
1234 with unit test code in the library sources:
1235
1236 ```output
1237 [workspace]/
1238 WORKSPACE
1239 hello_lib/
1240 BUILD
1241 src/
1242 lib.rs
1243 ```
1244
1245 `hello_lib/src/lib.rs`:
1246 ```rust
1247 pub struct Greeter {
1248 greeting: String,
1249 }
1250
1251 impl Greeter {
1252 pub fn new(greeting: &str) -> Greeter {
1253 Greeter { greeting: greeting.to_string(), }
1254 }
1255
1256 pub fn greet(&self, thing: &str) -> String {
1257 format!("{} {}", &self.greeting, thing)
1258 }
1259 }
1260
1261 #[cfg(test)]
1262 mod test {
1263 use super::Greeter;
1264
1265 #[test]
1266 fn test_greeting() {
1267 let hello = Greeter::new("Hi");
1268 assert_eq!("Hi Rust", hello.greet("Rust"));
1269 }
1270 }
1271 ```
1272
Brian Silverman5f6f2762022-08-13 19:30:05 -07001273 To build and run the tests, simply add a `rust_test` rule with no `srcs`
1274 and only depends on the `hello_lib` `rust_library` target via the
1275 `crate` attribute:
Brian Silvermancc09f182022-03-09 15:40:20 -08001276
1277 `hello_lib/BUILD`:
1278 ```python
1279 load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
1280
1281 rust_library(
1282 name = "hello_lib",
1283 srcs = ["src/lib.rs"],
1284 )
1285
1286 rust_test(
1287 name = "hello_lib_test",
Brian Silvermancc09f182022-03-09 15:40:20 -08001288 crate = ":hello_lib",
1289 # You may add other deps that are specific to the test configuration
1290 deps = ["//some/dev/dep"],
Adam Snaider1c095c92023-07-08 02:09:58 -04001291 )
Brian Silvermancc09f182022-03-09 15:40:20 -08001292 ```
1293
Brian Silverman5f6f2762022-08-13 19:30:05 -07001294 Run the test with `bazel test //hello_lib:hello_lib_test`. The crate
1295 will be built using the same crate name as the underlying ":hello_lib"
1296 crate.
1297
Brian Silvermancc09f182022-03-09 15:40:20 -08001298 ### Example: `test` directory
1299
1300 Integration tests that live in the [`tests` directory][int-tests], they are \
1301 essentially built as separate crates. Suppose you have the following directory \
1302 structure where `greeting.rs` is an integration test for the `hello_lib` \
1303 library crate:
1304
1305 [int-tests]: http://doc.rust-lang.org/book/testing.html#the-tests-directory
1306
1307 ```output
1308 [workspace]/
1309 WORKSPACE
1310 hello_lib/
1311 BUILD
1312 src/
1313 lib.rs
1314 tests/
1315 greeting.rs
1316 ```
1317
1318 `hello_lib/tests/greeting.rs`:
1319 ```rust
1320 extern crate hello_lib;
1321
1322 use hello_lib;
1323
1324 #[test]
1325 fn test_greeting() {
1326 let hello = greeter::Greeter::new("Hello");
1327 assert_eq!("Hello world", hello.greeting("world"));
1328 }
1329 ```
1330
1331 To build the `greeting.rs` integration test, simply add a `rust_test` target
1332 with `greeting.rs` in `srcs` and a dependency on the `hello_lib` target:
1333
1334 `hello_lib/BUILD`:
1335 ```python
1336 load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
1337
1338 rust_library(
1339 name = "hello_lib",
1340 srcs = ["src/lib.rs"],
1341 )
1342
1343 rust_test(
1344 name = "greeting_test",
1345 srcs = ["tests/greeting.rs"],
1346 deps = [":hello_lib"],
1347 )
1348 ```
1349
Brian Silverman5f6f2762022-08-13 19:30:05 -07001350 Run the test with `bazel test //hello_lib:greeting_test`.
Brian Silvermancc09f182022-03-09 15:40:20 -08001351"""),
1352)
1353
1354def rust_test_suite(name, srcs, **kwargs):
1355 """A rule for creating a test suite for a set of `rust_test` targets.
1356
1357 This rule can be used for setting up typical rust [integration tests][it]. Given the following
1358 directory structure:
1359
1360 ```text
1361 [crate]/
1362 BUILD.bazel
1363 src/
1364 lib.rs
1365 main.rs
1366 tests/
1367 integrated_test_a.rs
1368 integrated_test_b.rs
1369 integrated_test_c.rs
1370 patterns/
1371 fibonacci_test.rs
1372 ```
1373
1374 The rule can be used to generate [rust_test](#rust_test) targets for each source file under `tests`
1375 and a [test_suite][ts] which encapsulates all tests.
1376
1377 ```python
1378 load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test_suite")
1379
1380 rust_library(
1381 name = "math_lib",
1382 srcs = ["src/lib.rs"],
1383 )
1384
1385 rust_binary(
1386 name = "math_bin",
1387 srcs = ["src/main.rs"],
1388 )
1389
1390 rust_test_suite(
1391 name = "integrated_tests_suite",
1392 srcs = glob(["tests/**"]),
1393 deps = [":math_lib"],
1394 )
1395 ```
1396
1397 [it]: https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html
1398 [ts]: https://docs.bazel.build/versions/master/be/general.html#test_suite
1399
1400 Args:
1401 name (str): The name of the `test_suite`.
1402 srcs (list): All test sources, typically `glob(["tests/**/*.rs"])`.
1403 **kwargs (dict): Additional keyword arguments for the underyling [rust_test](#rust_test) targets. The
1404 `tags` argument is also passed to the generated `test_suite` target.
1405 """
1406 tests = []
1407
1408 for src in srcs:
1409 if not src.endswith(".rs"):
1410 fail("srcs should have `.rs` extensions")
1411
1412 # Prefixed with `name` to allow parameterization with macros
1413 # The test name should not end with `.rs`
1414 test_name = name + "_" + src[:-3]
1415 rust_test(
1416 name = test_name,
Brian Silvermancc09f182022-03-09 15:40:20 -08001417 srcs = [src],
1418 **kwargs
1419 )
1420 tests.append(test_name)
1421
1422 native.test_suite(
1423 name = name,
1424 tests = tests,
1425 tags = kwargs.get("tags", None),
1426 )
Adam Snaider1c095c92023-07-08 02:09:58 -04001427
1428rust_library_group = rule(
1429 implementation = _rust_library_group_impl,
1430 provides = [rust_common.crate_group_info],
1431 attrs = {
1432 "deps": attr.label_list(
1433 doc = "Other dependencies to forward through this crate group.",
1434 providers = [[rust_common.crate_group_info], [rust_common.crate_info]],
1435 ),
1436 },
1437 doc = dedent("""\
1438 Functions as an alias for a set of dependencies.
1439
1440 Specifically, the following are equivalent:
1441
1442 ```starlark
1443 rust_library_group(
1444 name = "crate_group",
1445 deps = [
1446 ":crate1",
1447 ":crate2",
1448 ],
1449 )
1450
1451 rust_library(
1452 name = "foobar",
1453 deps = [":crate_group"],
1454 ...
1455 )
1456 ```
1457
1458 and
1459
1460 ```starlark
1461 rust_library(
1462 name = "foobar",
1463 deps = [
1464 ":crate1",
1465 ":crate2",
1466 ],
1467 ...
1468 )
1469 ```
1470 """),
1471)