Skip to content

Commit 820ad5d

Browse files
zhengwei143copybara-github
authored andcommitted
When evaluating an artifact (directory) generated by an action template, evaluate only the expanded action execution keys of actions that generate that directory.
The (rough) related Skyframe logic that leads to this in the ArtifactFunction is as follows: 1. Requests for the output directory 2. Gets the actions to that will generate the files in the output directory after evaluating the ActionTemplateExpansionKey for the ActionTemplate. 3. Requests for these actions to be evaluated. 4. Pieces the output files from the ActionExecutionValue(s) into the tree artifact The previous logic requests the execution of all actions (at step 3) generated by the template, and expects that all actions generated by the action template will output a file in the output directory (at step 4). We break this requirement by only evaluating the actions that output files within the directory (at step 3). This allows us to have 2 actions generate output files in two output directories separately in with Starlarkified action templates (instead of requiring that every action generate outputs in both output directories, which is the implication of the existing logic). PiperOrigin-RevId: 808644280 Change-Id: I4412ab4a92b703a1e0a2ad3fbb224978799b523d
1 parent d811376 commit 820ad5d

File tree

3 files changed

+178
-3
lines changed

3 files changed

+178
-3
lines changed

src/main/java/com/google/devtools/build/lib/skyframe/ArtifactFunction.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,15 @@ public ImmutableList<ActionLookupData> getActionTemplateExpansionKeys(
494494
ImmutableList.Builder<ActionLookupData> expandedActionExecutionKeys =
495495
ImmutableList.builderWithExpectedSize(value.getActions().size());
496496
for (ActionAnalysisMetadata action : value.getActions()) {
497-
expandedActionExecutionKeys.add(
498-
((DerivedArtifact) action.getPrimaryOutput()).getGeneratingActionKey());
497+
// ActionTemplates expand into actions that can generate multiple output trees (as a whole),
498+
// but an expanded action can generate outputs under only a single tree. As such, we only
499+
// need to evaluate the action if it generates an output under the requested tree artifact.
500+
for (Artifact output : action.getOutputs()) {
501+
if (output.hasParent() && output.getParent().equals(artifact)) {
502+
expandedActionExecutionKeys.add(((DerivedArtifact) output).getGeneratingActionKey());
503+
break;
504+
}
505+
}
499506
}
500507
return expandedActionExecutionKeys.build();
501508
}

src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,17 @@
1919
import com.google.common.base.Preconditions;
2020
import com.google.common.collect.ImmutableList;
2121
import com.google.common.collect.ImmutableMap;
22+
import com.google.common.collect.ImmutableSet;
2223
import com.google.common.collect.Iterables;
2324
import com.google.devtools.build.lib.actions.Action;
2425
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
2526
import com.google.devtools.build.lib.actions.ActionConflictException;
27+
import com.google.devtools.build.lib.actions.ActionExecutionContext;
28+
import com.google.devtools.build.lib.actions.ActionKeyContext;
2629
import com.google.devtools.build.lib.actions.ActionLookupData;
30+
import com.google.devtools.build.lib.actions.ActionLookupKey;
2731
import com.google.devtools.build.lib.actions.ActionLookupValue;
32+
import com.google.devtools.build.lib.actions.ActionOwner;
2833
import com.google.devtools.build.lib.actions.ActionTemplate;
2934
import com.google.devtools.build.lib.actions.Actions;
3035
import com.google.devtools.build.lib.actions.Artifact;
@@ -36,13 +41,16 @@
3641
import com.google.devtools.build.lib.actions.ArtifactRoot.RootType;
3742
import com.google.devtools.build.lib.actions.BasicActionLookupValue;
3843
import com.google.devtools.build.lib.actions.FileArtifactValue;
44+
import com.google.devtools.build.lib.actions.InputMetadataProvider;
3945
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
4046
import com.google.devtools.build.lib.actions.util.TestAction.DummyAction;
4147
import com.google.devtools.build.lib.analysis.actions.SpawnActionTemplate;
48+
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
4249
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
4350
import com.google.devtools.build.lib.collect.nestedset.Order;
4451
import com.google.devtools.build.lib.events.NullEventHandler;
4552
import com.google.devtools.build.lib.skyframe.ArtifactFunction.SourceArtifactException;
53+
import com.google.devtools.build.lib.util.Fingerprint;
4654
import com.google.devtools.build.lib.vfs.FileStatus;
4755
import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter;
4856
import com.google.devtools.build.lib.vfs.Path;
@@ -61,6 +69,7 @@
6169
import java.util.Arrays;
6270
import java.util.HashMap;
6371
import java.util.Map;
72+
import javax.annotation.Nullable;
6473
import org.junit.Before;
6574
import org.junit.Test;
6675
import org.junit.runner.RunWith;
@@ -221,6 +230,50 @@ public void testConsecutiveSpawnActionTemplates() throws Throwable {
221230
assertThat(value.getChildValues().get(treeFileArtifact2).getDigest()).isNotNull();
222231
}
223232

233+
@Test
234+
public void testActionTemplateGeneratesMultipleOutputTreesFromDifferentActions()
235+
throws Throwable {
236+
// `inputTree` is a tree artifact generated by normal action.
237+
SpecialArtifact inputTree = createDerivedTreeArtifactWithAction("treeArtifact1");
238+
createFakeTreeFileArtifact(inputTree, "child1", "hello1");
239+
createFakeTreeFileArtifact(inputTree, "child2", "hello2");
240+
SpecialArtifact outputTree1 = createDerivedTreeArtifactOnly("treeArtifact2");
241+
SpecialArtifact outputTree2 = createDerivedTreeArtifactOnly("treeArtifact3");
242+
ActionTemplate<DummyAction> template =
243+
new TestActionTemplate(
244+
ImmutableList.of(inputTree), ImmutableSet.of(outputTree1, outputTree2)) {
245+
@Override
246+
public ImmutableList<DummyAction> generateActionsForInputArtifacts(
247+
ImmutableList<TreeFileArtifact> inputTreeFileArtifacts,
248+
ActionLookupKey artifactOwner) {
249+
ImmutableList.Builder<DummyAction> actions = ImmutableList.builder();
250+
for (SpecialArtifact outputTree : ImmutableSet.of(outputTree1, outputTree2)) {
251+
TreeFileArtifact output =
252+
TreeFileArtifact.createTemplateExpansionOutput(
253+
outputTree, "child", artifactOwner);
254+
actions.add(new DummyAction(NestedSetBuilder.emptySet(Order.STABLE_ORDER), output));
255+
}
256+
return actions.build();
257+
}
258+
};
259+
actions.add(template);
260+
TreeFileArtifact treeFileArtifact1 =
261+
createFakeExpansionTreeFileArtifact(template, outputTree1, "child", "hello");
262+
TreeFileArtifact treeFileArtifact2 =
263+
createFakeExpansionTreeFileArtifact(template, outputTree2, "child", "hello");
264+
TreeArtifactValue value = (TreeArtifactValue) evaluateArtifactValue(outputTree1);
265+
TreeArtifactValue value2 = (TreeArtifactValue) evaluateArtifactValue(outputTree2);
266+
267+
assertThat(value.getChildValues()).containsKey(treeFileArtifact1);
268+
assertThat(value2.getChildValues()).containsKey(treeFileArtifact2);
269+
// The TreeArtifactValue for outputTree1 should not contain the child from outputTree2 and vice
270+
// versa.
271+
assertThat(value.getChildValues()).doesNotContainKey(treeFileArtifact2);
272+
assertThat(value2.getChildValues()).doesNotContainKey(treeFileArtifact1);
273+
assertThat(value.getChildValues().get(treeFileArtifact1).getDigest()).isNotNull();
274+
assertThat(value2.getChildValues().get(treeFileArtifact2).getDigest()).isNotNull();
275+
}
276+
224277
private static void file(Path path, String contents) throws Exception {
225278
path.getParentDirectory().createDirectoryAndParents();
226279
writeFile(path, contents);
@@ -395,4 +448,116 @@ public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedExcept
395448
ImmutableMap.copyOf(artifactData), ImmutableMap.copyOf(treeArtifactData));
396449
}
397450
}
451+
452+
private abstract static class TestActionTemplate implements ActionTemplate<DummyAction> {
453+
private final ImmutableList<SpecialArtifact> inputTreeArtifacts;
454+
private final ImmutableSet<SpecialArtifact> outputTreeArtifacts;
455+
456+
TestActionTemplate(
457+
ImmutableList<SpecialArtifact> inputTreeArtifacts,
458+
ImmutableSet<SpecialArtifact> outputTreeArtifacts) {
459+
for (SpecialArtifact inputTreeArtifact : inputTreeArtifacts) {
460+
Preconditions.checkArgument(inputTreeArtifact.isTreeArtifact(), inputTreeArtifact);
461+
}
462+
for (SpecialArtifact outputTreeArtifact : outputTreeArtifacts) {
463+
Preconditions.checkArgument(outputTreeArtifact.isTreeArtifact(), outputTreeArtifact);
464+
}
465+
this.inputTreeArtifacts = inputTreeArtifacts;
466+
this.outputTreeArtifacts = outputTreeArtifacts;
467+
}
468+
469+
@Override
470+
public ImmutableList<SpecialArtifact> getInputTreeArtifacts() {
471+
return inputTreeArtifacts;
472+
}
473+
474+
@Override
475+
public ImmutableSet<Artifact> getOutputs() {
476+
return ImmutableSet.copyOf(outputTreeArtifacts);
477+
}
478+
479+
@Override
480+
public ActionOwner getOwner() {
481+
return ActionsTestUtil.NULL_ACTION_OWNER;
482+
}
483+
484+
@Override
485+
public boolean isShareable() {
486+
return false;
487+
}
488+
489+
@Override
490+
public String getMnemonic() {
491+
return "TestActionTemplate";
492+
}
493+
494+
@Override
495+
public String getKey(
496+
ActionKeyContext actionKeyContext, @Nullable InputMetadataProvider inputMetadataProvider) {
497+
Fingerprint fp = new Fingerprint();
498+
for (SpecialArtifact inputTreeArtifact : inputTreeArtifacts) {
499+
fp.addPath(inputTreeArtifact.getPath());
500+
}
501+
for (SpecialArtifact outputTreeArtifact : outputTreeArtifacts) {
502+
fp.addPath(outputTreeArtifact.getPath());
503+
}
504+
return fp.hexDigestAndReset();
505+
}
506+
507+
@Override
508+
public String prettyPrint() {
509+
return "TestActionTemplate for " + outputTreeArtifacts;
510+
}
511+
512+
@Override
513+
public String describe() {
514+
return prettyPrint();
515+
}
516+
517+
@Override
518+
public NestedSet<Artifact> getTools() {
519+
return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
520+
}
521+
522+
@Override
523+
public NestedSet<Artifact> getInputs() {
524+
return NestedSetBuilder.wrap(Order.STABLE_ORDER, inputTreeArtifacts);
525+
}
526+
527+
@Override
528+
public NestedSet<Artifact> getOriginalInputs() {
529+
return getInputs();
530+
}
531+
532+
@Override
533+
public NestedSet<Artifact> getSchedulingDependencies() {
534+
return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
535+
}
536+
537+
@Override
538+
public ImmutableList<String> getClientEnvironmentVariables() {
539+
return ImmutableList.of();
540+
}
541+
542+
@Override
543+
public NestedSet<Artifact> getInputFilesForExtraAction(
544+
ActionExecutionContext actionExecutionContext) {
545+
return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
546+
}
547+
548+
@Override
549+
public ImmutableSet<Artifact> getMandatoryOutputs() {
550+
return ImmutableSet.of();
551+
}
552+
553+
@Override
554+
public NestedSet<Artifact> getMandatoryInputs() {
555+
return NestedSetBuilder.wrap(Order.STABLE_ORDER, inputTreeArtifacts);
556+
}
557+
558+
@Override
559+
public String toString() {
560+
return prettyPrint();
561+
}
562+
}
398563
}

src/test/java/com/google/devtools/build/lib/skyframe/BUILD

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ java_test(
775775
":artifact_function_test_case",
776776
"//src/main/java/com/google/devtools/build/lib/actions",
777777
"//src/main/java/com/google/devtools/build/lib/actions:action_lookup_data",
778+
"//src/main/java/com/google/devtools/build/lib/actions:action_lookup_key",
778779
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
779780
"//src/main/java/com/google/devtools/build/lib/actions:file_metadata",
780781
"//src/main/java/com/google/devtools/build/lib/analysis:actions/spawn_action_template",
@@ -786,12 +787,15 @@ java_test(
786787
"//src/main/java/com/google/devtools/build/lib/skyframe:artifact_function",
787788
"//src/main/java/com/google/devtools/build/lib/skyframe:skyframe_cluster",
788789
"//src/main/java/com/google/devtools/build/lib/skyframe:tree_artifact_value",
790+
"//src/main/java/com/google/devtools/build/lib/util",
789791
"//src/main/java/com/google/devtools/build/lib/vfs",
790792
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
791793
"//src/main/java/com/google/devtools/build/skyframe",
792794
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
793795
"//src/test/java/com/google/devtools/build/lib/actions/util",
796+
"//third_party:error_prone_annotations",
794797
"//third_party:guava",
798+
"//third_party:jsr305",
795799
"//third_party:junit4",
796800
"//third_party:truth",
797801
],
@@ -1300,7 +1304,6 @@ java_test(
13001304
"//src/main/java/com/google/devtools/build/lib/actions:file_metadata",
13011305
"//src/main/java/com/google/devtools/build/lib/analysis:actions/custom_command_line",
13021306
"//src/main/java/com/google/devtools/build/lib/analysis:actions/spawn_action_template",
1303-
"//src/main/java/com/google/devtools/build/lib/bugreport",
13041307
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
13051308
"//src/main/java/com/google/devtools/build/lib/events",
13061309
"//src/main/java/com/google/devtools/build/lib/pkgcache",

0 commit comments

Comments
 (0)