15
15
16
16
import static com .google .devtools .build .lib .packages .semantics .BuildLanguageOptions .EXPERIMENTAL_SIBLING_REPOSITORY_LAYOUT ;
17
17
18
+ import com .google .common .base .Joiner ;
18
19
import com .google .common .collect .ImmutableList ;
19
20
import com .google .common .collect .ImmutableMap ;
21
+ import com .google .common .collect .Sets ;
20
22
import com .google .devtools .build .lib .actions .Action ;
21
23
import com .google .devtools .build .lib .actions .ActionAnalysisMetadata ;
22
24
import com .google .devtools .build .lib .actions .ActionLookupKey ;
23
25
import com .google .devtools .build .lib .actions .ActionRegistry ;
24
26
import com .google .devtools .build .lib .actions .Artifact ;
25
27
import com .google .devtools .build .lib .actions .ArtifactRoot ;
26
28
import com .google .devtools .build .lib .actions .CommandLine ;
29
+ import com .google .devtools .build .lib .actions .ExecException ;
27
30
import com .google .devtools .build .lib .actions .ParamFileInfo ;
31
+ import com .google .devtools .build .lib .actions .ResourceSet ;
32
+ import com .google .devtools .build .lib .actions .ResourceSetOrBuilder ;
28
33
import com .google .devtools .build .lib .actions .RunfilesSupplier ;
34
+ import com .google .devtools .build .lib .actions .UserExecException ;
29
35
import com .google .devtools .build .lib .actions .extra .ExtraActionInfo ;
30
36
import com .google .devtools .build .lib .actions .extra .SpawnInfo ;
31
37
import com .google .devtools .build .lib .analysis .BashCommandConstructor ;
49
55
import com .google .devtools .build .lib .collect .nestedset .Order ;
50
56
import com .google .devtools .build .lib .packages .TargetUtils ;
51
57
import com .google .devtools .build .lib .packages .semantics .BuildLanguageOptions ;
58
+ import com .google .devtools .build .lib .server .FailureDetails ;
59
+ import com .google .devtools .build .lib .server .FailureDetails .FailureDetail ;
60
+ import com .google .devtools .build .lib .server .FailureDetails .Interrupted ;
52
61
import com .google .devtools .build .lib .skyframe .serialization .autocodec .AutoCodec ;
53
62
import com .google .devtools .build .lib .skyframe .serialization .autocodec .SerializationConstant ;
54
63
import com .google .devtools .build .lib .starlarkbuildapi .FileApi ;
55
64
import com .google .devtools .build .lib .starlarkbuildapi .StarlarkActionFactoryApi ;
65
+ import com .google .devtools .build .lib .util .OS ;
56
66
import com .google .devtools .build .lib .vfs .PathFragment ;
57
67
import com .google .protobuf .GeneratedMessage ;
58
68
import java .nio .charset .StandardCharsets ;
59
69
import java .util .ArrayList ;
70
+ import java .util .Arrays ;
71
+ import java .util .HashSet ;
60
72
import java .util .List ;
61
73
import java .util .Map ;
62
74
import java .util .Optional ;
75
+ import java .util .Set ;
63
76
import java .util .UUID ;
64
77
import net .starlark .java .eval .Dict ;
65
78
import net .starlark .java .eval .EvalException ;
79
+ import net .starlark .java .eval .Mutability ;
66
80
import net .starlark .java .eval .Printer ;
67
81
import net .starlark .java .eval .Sequence ;
68
82
import net .starlark .java .eval .Starlark ;
83
+ import net .starlark .java .eval .StarlarkCallable ;
84
+ import net .starlark .java .eval .StarlarkFloat ;
85
+ import net .starlark .java .eval .StarlarkFunction ;
86
+ import net .starlark .java .eval .StarlarkInt ;
69
87
import net .starlark .java .eval .StarlarkSemantics ;
70
88
import net .starlark .java .eval .StarlarkThread ;
71
89
@@ -75,6 +93,10 @@ public class StarlarkActionFactory implements StarlarkActionFactoryApi {
75
93
/** Counter for actions.run_shell helper scripts. Every script must have a unique name. */
76
94
private int runShellOutputCounter = 0 ;
77
95
96
+ private static final ResourceSet DEFAULT_RESOURCE_SET = ResourceSet .createWithRamCpu (250 , 1 );
97
+ private static final Set <String > validResources =
98
+ new HashSet <>(Arrays .asList ("cpu" , "memory" , "local_test" ));
99
+
78
100
public StarlarkActionFactory (StarlarkRuleContext context ) {
79
101
this .context = context ;
80
102
}
@@ -339,7 +361,8 @@ public void run(
339
361
Object executionRequirementsUnchecked ,
340
362
Object inputManifestsUnchecked ,
341
363
Object execGroupUnchecked ,
342
- Object shadowedActionUnchecked )
364
+ Object shadowedActionUnchecked ,
365
+ Object resourceSetUnchecked )
343
366
throws EvalException {
344
367
context .checkMutable ("actions.run" );
345
368
@@ -377,6 +400,7 @@ public void run(
377
400
inputManifestsUnchecked ,
378
401
execGroupUnchecked ,
379
402
shadowedActionUnchecked ,
403
+ resourceSetUnchecked ,
380
404
builder );
381
405
}
382
406
@@ -430,7 +454,8 @@ public void runShell(
430
454
Object executionRequirementsUnchecked ,
431
455
Object inputManifestsUnchecked ,
432
456
Object execGroupUnchecked ,
433
- Object shadowedActionUnchecked )
457
+ Object shadowedActionUnchecked ,
458
+ Object resourceSetUnchecked )
434
459
throws EvalException {
435
460
context .checkMutable ("actions.run_shell" );
436
461
RuleContext ruleContext = getRuleContext ();
@@ -497,6 +522,7 @@ public void runShell(
497
522
inputManifestsUnchecked ,
498
523
execGroupUnchecked ,
499
524
shadowedActionUnchecked ,
525
+ resourceSetUnchecked ,
500
526
builder );
501
527
}
502
528
@@ -543,6 +569,7 @@ private void registerStarlarkAction(
543
569
Object inputManifestsUnchecked ,
544
570
Object execGroupUnchecked ,
545
571
Object shadowedActionUnchecked ,
572
+ Object resourceSetUnchecked ,
546
573
StarlarkAction .Builder builder )
547
574
throws EvalException {
548
575
if (inputs instanceof Sequence ) {
@@ -648,10 +675,127 @@ private void registerStarlarkAction(
648
675
builder .setShadowedAction (Optional .of ((Action ) shadowedActionUnchecked ));
649
676
}
650
677
678
+ if (getSemantics ().getBool (BuildLanguageOptions .EXPERIMENTAL_ACTION_RESOURCE_SET )
679
+ && resourceSetUnchecked != Starlark .NONE ) {
680
+ validateResourceSetBuilder (resourceSetUnchecked );
681
+ builder .setResources (
682
+ new StarlarkActionResourceSetBuilder (
683
+ (StarlarkFunction ) resourceSetUnchecked , mnemonic , getSemantics ()));
684
+ }
685
+
651
686
// Always register the action
652
687
registerAction (builder .build (ruleContext ));
653
688
}
654
689
690
+ private static class StarlarkActionResourceSetBuilder implements ResourceSetOrBuilder {
691
+ private final StarlarkCallable fn ;
692
+ private final String mnemonic ;
693
+ private final StarlarkSemantics semantics ;
694
+
695
+ public StarlarkActionResourceSetBuilder (
696
+ StarlarkCallable fn , String mnemonic , StarlarkSemantics semantics ) {
697
+ this .fn = fn ;
698
+ this .mnemonic = mnemonic ;
699
+ this .semantics = semantics ;
700
+ }
701
+
702
+ @ Override
703
+ public ResourceSet buildResourceSet (OS os , int inputsSize ) throws ExecException {
704
+ try (Mutability mu = Mutability .create ("resource_set_builder_function" )) {
705
+ StarlarkThread thread = new StarlarkThread (mu , semantics );
706
+ StarlarkInt inputInt = StarlarkInt .of (inputsSize );
707
+ Object response =
708
+ Starlark .call (
709
+ thread ,
710
+ this .fn ,
711
+ ImmutableList .of (os .getCanonicalName (), inputInt ),
712
+ ImmutableMap .of ());
713
+ Map <String , Object > resourceSetMapRaw =
714
+ Dict .cast (response , String .class , Object .class , "resource_set" );
715
+
716
+ if (!validResources .containsAll (resourceSetMapRaw .keySet ())) {
717
+ String message =
718
+ String .format (
719
+ "Illegal resource keys: (%s)" ,
720
+ Joiner .on ("," ).join (Sets .difference (resourceSetMapRaw .keySet (), validResources )));
721
+ throw new EvalException (message );
722
+ }
723
+
724
+ return ResourceSet .create (
725
+ getNumericOrDefault (resourceSetMapRaw , "memory" , DEFAULT_RESOURCE_SET .getMemoryMb ()),
726
+ getNumericOrDefault (resourceSetMapRaw , "cpu" , DEFAULT_RESOURCE_SET .getCpuUsage ()),
727
+ (int )
728
+ getNumericOrDefault (
729
+ resourceSetMapRaw ,
730
+ "local_test" ,
731
+ (double ) DEFAULT_RESOURCE_SET .getLocalTestCount ()));
732
+ } catch (EvalException e ) {
733
+ throw new UserExecException (
734
+ FailureDetail .newBuilder ()
735
+ .setMessage (
736
+ String .format ("Could not build resources for %s. %s" , mnemonic , e .getMessage ()))
737
+ .setStarlarkAction (
738
+ FailureDetails .StarlarkAction .newBuilder ()
739
+ .setCode (FailureDetails .StarlarkAction .Code .STARLARK_ACTION_UNKNOWN )
740
+ .build ())
741
+ .build ());
742
+ } catch (InterruptedException e ) {
743
+ throw new UserExecException (
744
+ FailureDetail .newBuilder ()
745
+ .setMessage (e .getMessage ())
746
+ .setInterrupted (
747
+ Interrupted .newBuilder ().setCode (Interrupted .Code .INTERRUPTED ).build ())
748
+ .build ());
749
+ }
750
+ }
751
+
752
+ private static double getNumericOrDefault (
753
+ Map <String , Object > resourceSetMap , String key , double defaultValue ) throws EvalException {
754
+ if (!resourceSetMap .containsKey (key )) {
755
+ return defaultValue ;
756
+ }
757
+
758
+ Object value = resourceSetMap .get (key );
759
+ if (value instanceof StarlarkInt ) {
760
+ return ((StarlarkInt ) value ).toDouble ();
761
+ }
762
+
763
+ if (value instanceof StarlarkFloat ) {
764
+ return ((StarlarkFloat ) value ).toDouble ();
765
+ }
766
+ throw new EvalException (
767
+ String .format (
768
+ "Illegal resource value type for key %s: got %s, want int or float" ,
769
+ key , Starlark .type (value )));
770
+ }
771
+ }
772
+
773
+ private static StarlarkFunction validateResourceSetBuilder (Object fn ) throws EvalException {
774
+ if (!(fn instanceof StarlarkFunction )) {
775
+ throw Starlark .errorf (
776
+ "resource_set should be a Starlark-defined function, but got %s instead" ,
777
+ Starlark .type (fn ));
778
+ }
779
+
780
+ StarlarkFunction sfn = (StarlarkFunction ) fn ;
781
+
782
+ // Reject non-global functions, because arbitrary closures may cause large
783
+ // analysis-phase data structures to remain live into the execution phase.
784
+ // We require that the function is "global" as opposed to "not a closure"
785
+ // because a global function may be closure if it refers to load bindings.
786
+ // This unfortunately disallows such trivially safe non-global
787
+ // functions as "lambda x: x".
788
+ // See https://github.com/bazelbuild/bazel/issues/12701.
789
+ if (sfn .getModule ().getGlobal (sfn .getName ()) != sfn ) {
790
+ throw Starlark .errorf (
791
+ "to avoid unintended retention of analysis data structures, "
792
+ + "the resource_set function (declared at %s) must be declared "
793
+ + "by a top-level def statement" ,
794
+ sfn .getLocation ());
795
+ }
796
+ return (StarlarkFunction ) fn ;
797
+ }
798
+
655
799
private String getMnemonic (Object mnemonicUnchecked ) {
656
800
String mnemonic = mnemonicUnchecked == Starlark .NONE ? "Action" : (String ) mnemonicUnchecked ;
657
801
if (getRuleContext ().getConfiguration ().getReservedActionMnemonics ().contains (mnemonic )) {
0 commit comments