blob: 583dc6e5ee497c17c2d8951e32908332a48d5b8b [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
15"""Utility functions not specific to the rust toolchain."""
16
17load("@bazel_tools//tools/cpp:toolchain_utils.bzl", find_rules_cc_toolchain = "find_cpp_toolchain")
18load(":providers.bzl", "BuildInfo", "CrateInfo", "DepInfo", "DepVariantInfo")
19
Brian Silverman5f6f2762022-08-13 19:30:05 -070020UNSUPPORTED_FEATURES = [
21 "thin_lto",
22 "module_maps",
23 "use_header_modules",
24 "fdo_instrument",
25 "fdo_optimize",
26]
27
Brian Silvermancc09f182022-03-09 15:40:20 -080028def find_toolchain(ctx):
29 """Finds the first rust toolchain that is configured.
30
31 Args:
32 ctx (ctx): The ctx object for the current target.
33
34 Returns:
35 rust_toolchain: A Rust toolchain context.
36 """
Brian Silverman5f6f2762022-08-13 19:30:05 -070037 return ctx.toolchains[Label("//rust:toolchain_type")]
Brian Silvermancc09f182022-03-09 15:40:20 -080038
39def find_cc_toolchain(ctx):
40 """Extracts a CcToolchain from the current target's context
41
42 Args:
43 ctx (ctx): The current target's rule context object
44
45 Returns:
46 tuple: A tuple of (CcToolchain, FeatureConfiguration)
47 """
48 cc_toolchain = find_rules_cc_toolchain(ctx)
49
50 feature_configuration = cc_common.configure_features(
51 ctx = ctx,
52 cc_toolchain = cc_toolchain,
53 requested_features = ctx.features,
Brian Silverman5f6f2762022-08-13 19:30:05 -070054 unsupported_features = UNSUPPORTED_FEATURES + ctx.disabled_features,
Brian Silvermancc09f182022-03-09 15:40:20 -080055 )
56 return cc_toolchain, feature_configuration
57
58# TODO: Replace with bazel-skylib's `path.dirname`. This requires addressing some
59# dependency issues or generating docs will break.
60def relativize(path, start):
61 """Returns the relative path from start to path.
62
63 Args:
64 path (str): The path to relativize.
65 start (str): The ancestor path against which to relativize.
66
67 Returns:
68 str: The portion of `path` that is relative to `start`.
69 """
70 src_parts = _path_parts(start)
71 dest_parts = _path_parts(path)
72 n = 0
73 for src_part, dest_part in zip(src_parts, dest_parts):
74 if src_part != dest_part:
75 break
76 n += 1
77
78 relative_path = ""
79 for _ in range(n, len(src_parts)):
80 relative_path += "../"
81 relative_path += "/".join(dest_parts[n:])
82
83 return relative_path
84
85def _path_parts(path):
86 """Takes a path and returns a list of its parts with all "." elements removed.
87
88 The main use case of this function is if one of the inputs to relativize()
89 is a relative path, such as "./foo".
90
91 Args:
92 path (str): A string representing a unix path
93
94 Returns:
95 list: A list containing the path parts with all "." elements removed.
96 """
97 path_parts = path.split("/")
98 return [part for part in path_parts if part != "."]
99
Brian Silverman5f6f2762022-08-13 19:30:05 -0700100def get_lib_name_default(lib):
101 """Returns the name of a library artifact.
Brian Silvermancc09f182022-03-09 15:40:20 -0800102
103 Args:
104 lib (File): A library file
105
106 Returns:
107 str: The name of the library
108 """
109 # On macos and windows, dynamic/static libraries always end with the
110 # extension and potential versions will be before the extension, and should
111 # be part of the library name.
112 # On linux, the version usually comes after the extension.
113 # So regardless of the platform we want to find the extension and make
114 # everything left to it the library name.
115
116 # Search for the extension - starting from the right - by removing any
117 # trailing digit.
118 comps = lib.basename.split(".")
119 for comp in reversed(comps):
120 if comp.isdigit():
121 comps.pop()
122 else:
123 break
124
125 # The library name is now everything minus the extension.
126 libname = ".".join(comps[:-1])
127
128 if libname.startswith("lib"):
129 return libname[3:]
130 else:
131 return libname
132
Brian Silverman5f6f2762022-08-13 19:30:05 -0700133# TODO: Could we remove this function in favor of a "windows" parameter in the
134# above function? It looks like currently lambdas cannot accept local parameters
135# so the following doesn't work:
136# args.add_all(
137# cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration),
138# map_each = lambda x: get_lib_name(x, for_windows = toolchain.os.startswith("windows)),
139# format_each = "-ldylib=%s",
140# )
141def get_lib_name_for_windows(lib):
142 """Returns the name of a library artifact for Windows builds.
143
144 Args:
145 lib (File): A library file
146
147 Returns:
148 str: The name of the library
149 """
150 # On macos and windows, dynamic/static libraries always end with the
151 # extension and potential versions will be before the extension, and should
152 # be part of the library name.
153 # On linux, the version usually comes after the extension.
154 # So regardless of the platform we want to find the extension and make
155 # everything left to it the library name.
156
157 # Search for the extension - starting from the right - by removing any
158 # trailing digit.
159 comps = lib.basename.split(".")
160 for comp in reversed(comps):
161 if comp.isdigit():
162 comps.pop()
163 else:
164 break
165
166 # The library name is now everything minus the extension.
167 libname = ".".join(comps[:-1])
168
169 return libname
170
Brian Silvermancc09f182022-03-09 15:40:20 -0800171def abs(value):
172 """Returns the absolute value of a number.
173
174 Args:
175 value (int): A number.
176
177 Returns:
178 int: The absolute value of the number.
179 """
180 if value < 0:
181 return -value
182 return value
183
184def determine_output_hash(crate_root, label):
185 """Generates a hash of the crate root file's path.
186
187 Args:
188 crate_root (File): The crate's root file (typically `lib.rs`).
189 label (Label): The label of the target.
190
191 Returns:
192 str: A string representation of the hash.
193 """
194
195 # Take the absolute value of hash() since it could be negative.
196 h = abs(hash(crate_root.path) + hash(repr(label)))
197 return repr(h)
198
199def get_preferred_artifact(library_to_link, use_pic):
200 """Get the first available library to link from a LibraryToLink object.
201
202 Args:
203 library_to_link (LibraryToLink): See the followg links for additional details:
204 https://docs.bazel.build/versions/master/skylark/lib/LibraryToLink.html
205 use_pic: If set, prefers pic_static_library over static_library.
206
207 Returns:
208 File: Returns the first valid library type (only one is expected)
209 """
210 if use_pic:
Brian Silverman5f6f2762022-08-13 19:30:05 -0700211 # Order consistent with https://github.com/bazelbuild/bazel/blob/815dfdabb7df31d4e99b6fc7616ff8e2f9385d98/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLinkingContext.java#L437.
Brian Silvermancc09f182022-03-09 15:40:20 -0800212 return (
213 library_to_link.pic_static_library or
Brian Silverman5f6f2762022-08-13 19:30:05 -0700214 library_to_link.static_library or
Brian Silvermancc09f182022-03-09 15:40:20 -0800215 library_to_link.interface_library or
216 library_to_link.dynamic_library
217 )
218 else:
219 return (
220 library_to_link.static_library or
221 library_to_link.pic_static_library or
222 library_to_link.interface_library or
223 library_to_link.dynamic_library
224 )
225
226def _expand_location(ctx, env, data):
Brian Silverman5f6f2762022-08-13 19:30:05 -0700227 """A trivial helper for `expand_dict_value_locations` and `expand_list_element_locations`
Brian Silvermancc09f182022-03-09 15:40:20 -0800228
229 Args:
230 ctx (ctx): The rule's context object
231 env (str): The value possibly containing location macros to expand.
Brian Silverman5f6f2762022-08-13 19:30:05 -0700232 data (sequence of Targets): See one of the parent functions.
Brian Silvermancc09f182022-03-09 15:40:20 -0800233
234 Returns:
235 string: The location-macro expanded version of the string.
236 """
237 for directive in ("$(execpath ", "$(location "):
238 if directive in env:
239 # build script runner will expand pwd to execroot for us
Brian Silverman5f6f2762022-08-13 19:30:05 -0700240 env = env.replace(directive, "$${pwd}/" + directive)
241 return ctx.expand_make_variables(
242 env,
243 ctx.expand_location(env, data),
244 {},
245 )
Brian Silvermancc09f182022-03-09 15:40:20 -0800246
247def expand_dict_value_locations(ctx, env, data):
248 """Performs location-macro expansion on string values.
249
250 $(execroot ...) and $(location ...) are prefixed with ${pwd},
251 which process_wrapper and build_script_runner will expand at run time
252 to the absolute path. This is necessary because include_str!() is relative
253 to the currently compiled file, and build scripts run relative to the
254 manifest dir, so we can not use execroot-relative paths.
255
256 $(rootpath ...) is unmodified, and is useful for passing in paths via
257 rustc_env that are encoded in the binary with env!(), but utilized at
258 runtime, such as in tests. The absolute paths are not usable in this case,
259 as compilation happens in a separate sandbox folder, so when it comes time
260 to read the file at runtime, the path is no longer valid.
261
Brian Silverman5f6f2762022-08-13 19:30:05 -0700262 For detailed documentation, see:
263 - [`expand_location`](https://bazel.build/rules/lib/ctx#expand_location)
264 - [`expand_make_variables`](https://bazel.build/rules/lib/ctx#expand_make_variables)
Brian Silvermancc09f182022-03-09 15:40:20 -0800265
266 Args:
267 ctx (ctx): The rule's context object
268 env (dict): A dict whose values we iterate over
269 data (sequence of Targets): The targets which may be referenced by
270 location macros. This is expected to be the `data` attribute of
271 the target, though may have other targets or attributes mixed in.
272
273 Returns:
274 dict: A dict of environment variables with expanded location macros
275 """
276 return dict([(k, _expand_location(ctx, v, data)) for (k, v) in env.items()])
277
278def expand_list_element_locations(ctx, args, data):
279 """Performs location-macro expansion on a list of string values.
280
281 $(execroot ...) and $(location ...) are prefixed with ${pwd},
282 which process_wrapper and build_script_runner will expand at run time
283 to the absolute path.
284
Brian Silverman5f6f2762022-08-13 19:30:05 -0700285 For detailed documentation, see:
286 - [`expand_location`](https://bazel.build/rules/lib/ctx#expand_location)
287 - [`expand_make_variables`](https://bazel.build/rules/lib/ctx#expand_make_variables)
Brian Silvermancc09f182022-03-09 15:40:20 -0800288
289 Args:
290 ctx (ctx): The rule's context object
291 args (list): A list we iterate over
292 data (sequence of Targets): The targets which may be referenced by
293 location macros. This is expected to be the `data` attribute of
294 the target, though may have other targets or attributes mixed in.
295
296 Returns:
297 list: A list of arguments with expanded location macros
298 """
299 return [_expand_location(ctx, arg, data) for arg in args]
300
301def name_to_crate_name(name):
302 """Converts a build target's name into the name of its associated crate.
303
304 Crate names cannot contain certain characters, such as -, which are allowed
305 in build target names. All illegal characters will be converted to
306 underscores.
307
308 This is a similar conversion as that which cargo does, taking a
309 `Cargo.toml`'s `package.name` and canonicalizing it
310
311 Note that targets can specify the `crate_name` attribute to customize their
312 crate name; in situations where this is important, use the
313 compute_crate_name() function instead.
314
315 Args:
316 name (str): The name of the target.
317
318 Returns:
319 str: The name of the crate for this target.
320 """
Brian Silverman5f6f2762022-08-13 19:30:05 -0700321 for illegal in ("-", "/"):
322 name = name.replace(illegal, "_")
323 return name
Brian Silvermancc09f182022-03-09 15:40:20 -0800324
325def _invalid_chars_in_crate_name(name):
326 """Returns any invalid chars in the given crate name.
327
328 Args:
329 name (str): Name to test.
330
331 Returns:
332 list: List of invalid characters in the crate name.
333 """
334
335 return dict([(c, ()) for c in name.elems() if not (c.isalnum() or c == "_")]).keys()
336
337def compute_crate_name(workspace_name, label, toolchain, name_override = None):
338 """Returns the crate name to use for the current target.
339
340 Args:
341 workspace_name (string): The current workspace name.
342 label (struct): The label of the current target.
343 toolchain (struct): The toolchain in use for the target.
344 name_override (String): An optional name to use (as an override of label.name).
345
346 Returns:
347 str: The crate name to use for this target.
348 """
349 if name_override:
350 invalid_chars = _invalid_chars_in_crate_name(name_override)
351 if invalid_chars:
352 fail("Crate name '{}' contains invalid character(s): {}".format(
353 name_override,
354 " ".join(invalid_chars),
355 ))
356 return name_override
357
358 if (toolchain and label and toolchain._rename_first_party_crates and
359 should_encode_label_in_crate_name(workspace_name, label, toolchain._third_party_dir)):
360 crate_name = encode_label_as_crate_name(label.package, label.name)
361 else:
362 crate_name = name_to_crate_name(label.name)
363
364 invalid_chars = _invalid_chars_in_crate_name(crate_name)
365 if invalid_chars:
366 fail(
367 "Crate name '{}' ".format(crate_name) +
368 "derived from Bazel target name '{}' ".format(label.name) +
369 "contains invalid character(s): {}\n".format(" ".join(invalid_chars)) +
370 "Consider adding a crate_name attribute to set a valid crate name",
371 )
372 return crate_name
373
374def dedent(doc_string):
375 """Remove any common leading whitespace from every line in text.
376
377 This functionality is similar to python's `textwrap.dedent` functionality
378 https://docs.python.org/3/library/textwrap.html#textwrap.dedent
379
380 Args:
381 doc_string (str): A docstring style string
382
383 Returns:
384 str: A string optimized for stardoc rendering
385 """
386 lines = doc_string.splitlines()
387 if not lines:
388 return doc_string
389
390 # If the first line is empty, use the second line
391 first_line = lines[0]
392 if not first_line:
393 first_line = lines[1]
394
395 # Detect how much space prepends the first line and subtract that from all lines
396 space_count = len(first_line) - len(first_line.lstrip())
397
398 # If there are no leading spaces, do not alter the docstring
399 if space_count == 0:
400 return doc_string
401 else:
402 # Remove the leading block of spaces from the current line
403 block = " " * space_count
404 return "\n".join([line.replace(block, "", 1).rstrip() for line in lines])
405
406def make_static_lib_symlink(actions, rlib_file):
407 """Add a .a symlink to an .rlib file.
408
409 The name of the symlink is derived from the <name> of the <name>.rlib file as follows:
410 * `<name>.a`, if <name> starts with `lib`
411 * `lib<name>.a`, otherwise.
412
413 For example, the name of the symlink for
414 * `libcratea.rlib` is `libcratea.a`
415 * `crateb.rlib` is `libcrateb.a`.
416
417 Args:
418 actions (actions): The rule's context actions object.
419 rlib_file (File): The file to symlink, which must end in .rlib.
420
421 Returns:
422 The symlink's File.
423 """
424 if not rlib_file.basename.endswith(".rlib"):
425 fail("file is not an .rlib: ", rlib_file.basename)
426 basename = rlib_file.basename[:-5]
427 if not basename.startswith("lib"):
428 basename = "lib" + basename
429 dot_a = actions.declare_file(basename + ".a", sibling = rlib_file)
430 actions.symlink(output = dot_a, target_file = rlib_file)
431 return dot_a
432
433def is_exec_configuration(ctx):
434 """Determine if a context is building for the exec configuration.
435
436 This is helpful when processing command line flags that should apply
437 to the target configuration but not the exec configuration.
438
439 Args:
440 ctx (ctx): The ctx object for the current target.
441
442 Returns:
443 True if the exec configuration is detected, False otherwise.
444 """
445
446 # TODO(djmarcin): Is there any better way to determine cfg=exec?
447 return ctx.genfiles_dir.path.find("-exec-") != -1
448
449def transform_deps(deps):
450 """Transforms a [Target] into [DepVariantInfo].
451
452 This helper function is used to transform ctx.attr.deps and ctx.attr.proc_macro_deps into
453 [DepVariantInfo].
454
455 Args:
456 deps (list of Targets): Dependencies coming from ctx.attr.deps or ctx.attr.proc_macro_deps
457
458 Returns:
459 list of DepVariantInfos.
460 """
461 return [DepVariantInfo(
462 crate_info = dep[CrateInfo] if CrateInfo in dep else None,
463 dep_info = dep[DepInfo] if DepInfo in dep else None,
464 build_info = dep[BuildInfo] if BuildInfo in dep else None,
465 cc_info = dep[CcInfo] if CcInfo in dep else None,
466 ) for dep in deps]
467
468def get_import_macro_deps(ctx):
469 """Returns a list of targets to be added to proc_macro_deps.
470
471 Args:
472 ctx (struct): the ctx of the current target.
473
474 Returns:
475 list of Targets. Either empty (if the fake import macro implementation
476 is being used), or a singleton list with the real implementation.
477 """
478 if ctx.attr._import_macro_dep.label.name == "fake_import_macro_impl":
479 return []
480
481 return [ctx.attr._import_macro_dep]
482
483def should_encode_label_in_crate_name(workspace_name, label, third_party_dir):
484 """Determines if the crate's name should include the Bazel label, encoded.
485
486 Crate names may only encode the label if the target is in the current repo,
487 the target is not in the third_party_dir, and the current repo is not
488 rules_rust.
489
490 Args:
491 workspace_name (string): The name of the current workspace.
492 label (Label): The package in question.
493 third_party_dir (string): The directory in which third-party packages are kept.
494
495 Returns:
496 True if the crate name should encode the label, False otherwise.
497 """
498
499 # TODO(hlopko): This code assumes a monorepo; make it work with external
500 # repositories as well.
501 return (
502 workspace_name != "rules_rust" and
503 not label.workspace_root and
504 not ("//" + label.package + "/").startswith(third_party_dir + "/")
505 )
506
507# This is a list of pairs, where the first element of the pair is a character
508# that is allowed in Bazel package or target names but not in crate names; and
509# the second element is an encoding of that char suitable for use in a crate
510# name.
511_encodings = (
Brian Silverman5f6f2762022-08-13 19:30:05 -0700512 (":", "x"),
513 ("!", "excl"),
514 ("%", "prc"),
515 ("@", "ao"),
Brian Silvermancc09f182022-03-09 15:40:20 -0800516 ("^", "caret"),
Brian Silverman5f6f2762022-08-13 19:30:05 -0700517 ("`", "bt"),
518 (" ", "sp"),
519 ("\"", "dq"),
520 ("#", "octo"),
521 ("$", "dllr"),
522 ("&", "amp"),
523 ("'", "sq"),
524 ("(", "lp"),
525 (")", "rp"),
526 ("*", "astr"),
527 ("-", "d"),
528 ("+", "pl"),
529 (",", "cm"),
530 (";", "sm"),
531 ("<", "la"),
532 ("=", "eq"),
533 (">", "ra"),
534 ("?", "qm"),
535 ("[", "lbk"),
536 ("]", "rbk"),
537 ("{", "lbe"),
538 ("|", "pp"),
539 ("}", "rbe"),
540 ("~", "td"),
541 ("/", "y"),
542 (".", "pd"),
Brian Silvermancc09f182022-03-09 15:40:20 -0800543)
544
545# For each of the above encodings, we generate two substitution rules: one that
546# ensures any occurrences of the encodings themselves in the package/target
547# aren't clobbered by this translation, and one that does the encoding itself.
548# We also include a rule that protects the clobbering-protection rules from
549# getting clobbered.
Brian Silverman5f6f2762022-08-13 19:30:05 -0700550_substitutions = [("_z", "_zz_")] + [
Brian Silvermancc09f182022-03-09 15:40:20 -0800551 subst
552 for (pattern, replacement) in _encodings
553 for subst in (
Brian Silverman5f6f2762022-08-13 19:30:05 -0700554 ("_{}_".format(replacement), "_z{}_".format(replacement)),
Brian Silvermancc09f182022-03-09 15:40:20 -0800555 (pattern, "_{}_".format(replacement)),
556 )
557]
558
Brian Silverman5f6f2762022-08-13 19:30:05 -0700559# Expose the substitutions for testing only.
560substitutions_for_testing = _substitutions
561
Brian Silvermancc09f182022-03-09 15:40:20 -0800562def encode_label_as_crate_name(package, name):
563 """Encodes the package and target names in a format suitable for a crate name.
564
565 Args:
566 package (string): The package of the target in question.
567 name (string): The name of the target in question.
568
569 Returns:
570 A string that encodes the package and target name, to be used as the crate's name.
571 """
Brian Silverman5f6f2762022-08-13 19:30:05 -0700572 return _encode_raw_string(package + ":" + name)
573
574def _encode_raw_string(str):
575 """Encodes a string using the above encoding format.
576
577 Args:
578 str (string): The string to be encoded.
579
580 Returns:
581 An encoded version of the input string.
582 """
583 return _replace_all(str, _substitutions)
584
585# Expose the underlying encoding function for testing only.
586encode_raw_string_for_testing = _encode_raw_string
Brian Silvermancc09f182022-03-09 15:40:20 -0800587
588def decode_crate_name_as_label_for_testing(crate_name):
589 """Decodes a crate_name that was encoded by encode_label_as_crate_name.
590
591 This is used to check that the encoding is bijective; it is expected to only
592 be used in tests.
593
594 Args:
595 crate_name (string): The name of the crate.
596
597 Returns:
598 A string representing the Bazel label (package and target).
599 """
600 return _replace_all(crate_name, [(t[1], t[0]) for t in _substitutions])
601
602def _replace_all(string, substitutions):
603 """Replaces occurrences of the given patterns in `string`.
604
605 There are a few reasons this looks complicated:
606 * The substitutions are performed with some priority, i.e. patterns that are
607 listed first in `substitutions` are higher priority than patterns that are
608 listed later.
609 * We also take pains to avoid doing replacements that overlap with each
610 other, since overlaps invalidate pattern matches.
611 * To avoid hairy offset invalidation, we apply the substitutions
612 right-to-left.
613 * To avoid the "_quote" -> "_quotequote_" rule introducing new pattern
614 matches later in the string during decoding, we take the leftmost
615 replacement, in cases of overlap. (Note that no rule can induce new
616 pattern matches *earlier* in the string.) (E.g. "_quotedot_" encodes to
617 "_quotequote_dot_". Note that "_quotequote_" and "_dot_" both occur in
618 this string, and overlap.).
619
620 Args:
621 string (string): the string in which the replacements should be performed.
622 substitutions: the list of patterns and replacements to apply.
623
624 Returns:
625 A string with the appropriate substitutions performed.
626 """
627
628 # Find the highest-priority pattern matches for each string index, going
629 # left-to-right and skipping indices that are already involved in a
630 # pattern match.
631 plan = {}
632 matched_indices_set = {}
633 for pattern_start in range(len(string)):
634 if pattern_start in matched_indices_set:
635 continue
636 for (pattern, replacement) in substitutions:
637 if not string.startswith(pattern, pattern_start):
638 continue
639 length = len(pattern)
640 plan[pattern_start] = (length, replacement)
641 matched_indices_set.update([(pattern_start + i, True) for i in range(length)])
642 break
643
644 # Execute the replacement plan, working from right to left.
645 for pattern_start in sorted(plan.keys(), reverse = True):
646 length, replacement = plan[pattern_start]
647 after_pattern = pattern_start + length
648 string = string[:pattern_start] + replacement + string[after_pattern:]
649
650 return string
Brian Silverman5f6f2762022-08-13 19:30:05 -0700651
652def can_build_metadata(toolchain, ctx, crate_type):
653 """Can we build metadata for this rust_library?
654
655 Args:
656 toolchain (toolchain): The rust toolchain
657 ctx (ctx): The rule's context object
658 crate_type (String): one of lib|rlib|dylib|staticlib|cdylib|proc-macro
659
660 Returns:
661 bool: whether we can build metadata for this rule.
662 """
663
664 # In order to enable pipelined compilation we require that:
665 # 1) The _pipelined_compilation flag is enabled,
666 # 2) the OS running the rule is something other than windows as we require sandboxing (for now),
667 # 3) process_wrapper is enabled (this is disabled when compiling process_wrapper itself),
668 # 4) the crate_type is rlib or lib.
669 return toolchain._pipelined_compilation and \
670 toolchain.os != "windows" and \
671 ctx.attr._process_wrapper and \
672 crate_type in ("rlib", "lib")