|
100 | 100 | import com.google.errorprone.annotations.FormatMethod;
|
101 | 101 | import java.util.Collection;
|
102 | 102 | import java.util.Map;
|
| 103 | +import java.util.regex.Pattern; |
103 | 104 | import javax.annotation.Nullable;
|
104 | 105 | import net.starlark.java.eval.Debug;
|
105 | 106 | import net.starlark.java.eval.Dict;
|
|
111 | 112 | import net.starlark.java.eval.StarlarkCallable;
|
112 | 113 | import net.starlark.java.eval.StarlarkFunction;
|
113 | 114 | import net.starlark.java.eval.StarlarkInt;
|
| 115 | +import net.starlark.java.eval.StarlarkList; |
114 | 116 | import net.starlark.java.eval.StarlarkThread;
|
115 | 117 | import net.starlark.java.eval.Tuple;
|
116 | 118 | import net.starlark.java.syntax.Identifier;
|
@@ -138,6 +140,7 @@ public Label load(String from) throws Exception {
|
138 | 140 | }
|
139 | 141 | }
|
140 | 142 | });
|
| 143 | + private static final Pattern RULE_NAME_PATTERN = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*"); |
141 | 144 |
|
142 | 145 | // TODO(bazel-team): Remove the code duplication (BaseRuleClasses and this class).
|
143 | 146 | /** Parent rule class for non-executable non-test Starlark rules. */
|
@@ -282,7 +285,7 @@ public Provider provider(String doc, Object fields, StarlarkThread thread) throw
|
282 | 285 |
|
283 | 286 | // TODO(bazel-team): implement attribute copy and other rule properties
|
284 | 287 | @Override
|
285 |
| - public StarlarkCallable rule( |
| 288 | + public StarlarkRuleFunction rule( |
286 | 289 | StarlarkFunction implementation,
|
287 | 290 | Boolean test,
|
288 | 291 | Object attrs,
|
@@ -507,6 +510,81 @@ public StarlarkCallable rule(
|
507 | 510 | return starlarkRuleFunction;
|
508 | 511 | }
|
509 | 512 |
|
| 513 | + @Override |
| 514 | + public void analysisTest( |
| 515 | + String name, |
| 516 | + StarlarkFunction implementation, |
| 517 | + Object attrs, |
| 518 | + Sequence<?> fragments, |
| 519 | + Sequence<?> toolchains, |
| 520 | + Object attrValuesApi, |
| 521 | + StarlarkThread thread) |
| 522 | + throws EvalException, InterruptedException { |
| 523 | + if (!RULE_NAME_PATTERN.matcher(name).matches()) { |
| 524 | + throw Starlark.errorf("'name' is limited to Starlark identifiers, got %s", name); |
| 525 | + } |
| 526 | + Dict<String, Object> attrValues = |
| 527 | + Dict.cast(attrValuesApi, String.class, Object.class, "attr_values"); |
| 528 | + if (attrValues.containsKey("name")) { |
| 529 | + throw Starlark.errorf("'name' cannot be set or overridden in 'attr_values'"); |
| 530 | + } |
| 531 | + |
| 532 | + StarlarkRuleFunction starlarkRuleFunction = |
| 533 | + rule( |
| 534 | + implementation, |
| 535 | + /*test=*/ true, |
| 536 | + attrs, |
| 537 | + /*implicitOutputs=*/ Starlark.NONE, |
| 538 | + /*executable=*/ false, |
| 539 | + /*outputToGenfiles=*/ false, |
| 540 | + /*fragments=*/ fragments, |
| 541 | + /*hostFragments=*/ StarlarkList.empty(), |
| 542 | + /*starlarkTestable=*/ false, |
| 543 | + /*toolchains=*/ toolchains, |
| 544 | + /*useToolchainTransition=*/ false, |
| 545 | + /*doc=*/ "", |
| 546 | + /*providesArg=*/ StarlarkList.empty(), |
| 547 | + /*execCompatibleWith=*/ StarlarkList.empty(), |
| 548 | + /*analysisTest=*/ Boolean.TRUE, |
| 549 | + /*buildSetting=*/ Starlark.NONE, |
| 550 | + /*cfg=*/ Starlark.NONE, |
| 551 | + /*execGroups=*/ Starlark.NONE, |
| 552 | + /*compileOneFiletype=*/ Starlark.NONE, |
| 553 | + /*name=*/ Starlark.NONE, |
| 554 | + thread); |
| 555 | + |
| 556 | + // Export the rule |
| 557 | + // Because exporting can raise multiple errors, we need to accumulate them here into a single |
| 558 | + // EvalException. This is a code smell because any non-ERROR events will be lost, and any |
| 559 | + // location information in the events will be overwritten by the location of this rule's |
| 560 | + // definition. |
| 561 | + // However, this is currently fine because StarlarkRuleFunction#export only creates events that |
| 562 | + // are ERRORs and that have the rule definition as their location. |
| 563 | + // TODO(brandjon): Instead of accumulating events here, consider registering the rule in the |
| 564 | + // BazelStarlarkContext, and exporting such rules after module evaluation in |
| 565 | + // BzlLoadFunction#execAndExport. |
| 566 | + PackageContext pkgContext = thread.getThreadLocal(PackageContext.class); |
| 567 | + StoredEventHandler handler = new StoredEventHandler(); |
| 568 | + starlarkRuleFunction.export( |
| 569 | + handler, pkgContext.getLabel(), name + "_test"); // export in BUILD thread |
| 570 | + if (handler.hasErrors()) { |
| 571 | + StringBuilder errors = |
| 572 | + handler.getEvents().stream() |
| 573 | + .filter(e -> e.getKind() == EventKind.ERROR) |
| 574 | + .reduce( |
| 575 | + new StringBuilder(), |
| 576 | + (sb, ev) -> sb.append("\n").append(ev.getMessage()), |
| 577 | + StringBuilder::append); |
| 578 | + throw Starlark.errorf("Errors in exporting %s: %s", name, errors.toString()); |
| 579 | + } |
| 580 | + |
| 581 | + // Instantiate the target |
| 582 | + Dict.Builder<String, Object> args = Dict.builder(); |
| 583 | + args.put("name", name); |
| 584 | + args.putAll(attrValues); |
| 585 | + starlarkRuleFunction.call(thread, Tuple.of(), args.buildImmutable()); |
| 586 | + } |
| 587 | + |
510 | 588 | /**
|
511 | 589 | * Returns the module (file) of the outermost enclosing Starlark function on the call stack or
|
512 | 590 | * null if none of the active calls are functions defined in Starlark.
|
|
0 commit comments