13
13
// limitations under the License.
14
14
package com .google .devtools .build .lib .exec ;
15
15
16
+ import static com .google .common .collect .ImmutableList .toImmutableList ;
16
17
17
18
import com .google .common .annotations .VisibleForTesting ;
18
19
import com .google .common .base .Preconditions ;
22
23
import com .google .devtools .build .lib .actions .Artifact ;
23
24
import com .google .devtools .build .lib .actions .Artifact .ArtifactExpander ;
24
25
import com .google .devtools .build .lib .actions .Artifact .MissingExpansionException ;
26
+ import com .google .devtools .build .lib .actions .Artifact .SpecialArtifact ;
25
27
import com .google .devtools .build .lib .actions .Artifact .TreeFileArtifact ;
26
28
import com .google .devtools .build .lib .actions .FileArtifactValue ;
27
29
import com .google .devtools .build .lib .actions .FilesetManifest ;
39
41
import com .google .devtools .build .lib .vfs .Path ;
40
42
import com .google .devtools .build .lib .vfs .PathFragment ;
41
43
import java .io .IOException ;
42
- import java .util .Arrays ;
43
44
import java .util .HashMap ;
44
45
import java .util .List ;
45
46
import java .util .Map ;
@@ -266,14 +267,19 @@ public SortedMap<PathFragment, ActionInput> getInputMapping(
266
267
267
268
/** The interface for accessing part of the input hierarchy. */
268
269
public interface InputWalker {
270
+
271
+ /** Returns the leaf nodes at this point in the hierarchy. */
269
272
SortedMap <PathFragment , ActionInput > getLeavesInputMapping ()
270
273
throws IOException , ForbiddenActionInputException ;
271
274
272
- void visitNonLeaves (InputVisitor visitor ) throws IOException , ForbiddenActionInputException ;
275
+ /** Invokes the visitor on the non-leaf nodes at this point in the hierarchy. */
276
+ default void visitNonLeaves (InputVisitor visitor )
277
+ throws IOException , ForbiddenActionInputException {}
273
278
}
274
279
275
280
/** The interface for visiting part of the input hierarchy. */
276
281
public interface InputVisitor {
282
+
277
283
/**
278
284
* Visits a part of the input hierarchy.
279
285
*
@@ -305,13 +311,8 @@ public void walkInputs(
305
311
306
312
RunfilesSupplier runfilesSupplier = spawn .getRunfilesSupplier ();
307
313
visitor .visit (
308
- // The list of variables affecting the functional expressions below.
309
- Arrays .asList (
310
- // Assuming that artifactExpander and actionInputFileCache, different for each spawn,
311
- // always expand the same way.
312
- this , // For accessing addRunfilesToInputs.
313
- runfilesSupplier ,
314
- baseDirectory ),
314
+ // Cache key for the sub-mapping containing the runfiles inputs for this spawn.
315
+ ImmutableList .of (runfilesSupplier , baseDirectory ),
315
316
new InputWalker () {
316
317
@ Override
317
318
public SortedMap <PathFragment , ActionInput > getLeavesInputMapping ()
@@ -321,20 +322,14 @@ public SortedMap<PathFragment, ActionInput> getLeavesInputMapping()
321
322
inputMap , runfilesSupplier , actionInputFileCache , artifactExpander , baseDirectory );
322
323
return inputMap ;
323
324
}
324
-
325
- @ Override
326
- public void visitNonLeaves (InputVisitor childVisitor ) {}
327
325
});
328
326
329
327
Map <Artifact , ImmutableList <FilesetOutputSymlink >> filesetMappings = spawn .getFilesetMappings ();
330
328
// filesetMappings is assumed to be very small, so no need to implement visitNonLeaves() for
331
329
// improved runtime.
332
330
visitor .visit (
333
- // The list of variables affecting the functional expressions below.
334
- Arrays .asList (
335
- this , // For accessing addFilesetManifests.
336
- filesetMappings ,
337
- baseDirectory ),
331
+ // Cache key for the sub-mapping containing the fileset inputs for this spawn.
332
+ ImmutableList .of (filesetMappings , baseDirectory ),
338
333
new InputWalker () {
339
334
@ Override
340
335
public SortedMap <PathFragment , ActionInput > getLeavesInputMapping ()
@@ -343,32 +338,32 @@ public SortedMap<PathFragment, ActionInput> getLeavesInputMapping()
343
338
addFilesetManifests (filesetMappings , inputMap , baseDirectory );
344
339
return inputMap ;
345
340
}
346
-
347
- @ Override
348
- public void visitNonLeaves (InputVisitor childVisitor ) {}
349
341
});
350
342
}
351
343
352
- /** Walks through one level of a {@link NestedSet} of {@link ActionInput}s . */
344
+ /** Visits a {@link NestedSet} occurring in {@link Spawn#getInputFiles} . */
353
345
private void walkNestedSetInputs (
354
346
PathFragment baseDirectory ,
355
347
NestedSet <? extends ActionInput > someInputFiles ,
356
348
ArtifactExpander artifactExpander ,
357
349
InputVisitor visitor )
358
350
throws IOException , ForbiddenActionInputException {
359
351
visitor .visit (
360
- // addInputs is static so no need to add 'this' as dependent key.
361
- Arrays .asList (
362
- // Assuming that artifactExpander, different for each spawn, always expands the same
363
- // way.
364
- someInputFiles .toNode (), baseDirectory ),
352
+ // Cache key for the sub-mapping containing the files in this nested set.
353
+ ImmutableList .of (someInputFiles .toNode (), baseDirectory ),
365
354
new InputWalker () {
366
355
@ Override
367
356
public SortedMap <PathFragment , ActionInput > getLeavesInputMapping () {
368
357
TreeMap <PathFragment , ActionInput > inputMap = new TreeMap <>();
358
+ // Consider files inside tree artifacts to be non-leaves. This caches better when a
359
+ // large tree is not the sole direct child of a nested set.
360
+ ImmutableList <? extends ActionInput > leaves =
361
+ someInputFiles .getLeaves ().stream ()
362
+ .filter (a -> !isTreeArtifact (a ))
363
+ .collect (toImmutableList ());
369
364
addInputs (
370
365
inputMap ,
371
- NestedSetBuilder .wrap (someInputFiles .getOrder (), someInputFiles . getLeaves () ),
366
+ NestedSetBuilder .wrap (someInputFiles .getOrder (), leaves ),
372
367
artifactExpander ,
373
368
baseDirectory );
374
369
return inputMap ;
@@ -377,18 +372,53 @@ public SortedMap<PathFragment, ActionInput> getLeavesInputMapping() {
377
372
@ Override
378
373
public void visitNonLeaves (InputVisitor childVisitor )
379
374
throws IOException , ForbiddenActionInputException {
375
+ for (ActionInput input : someInputFiles .getLeaves ()) {
376
+ if (isTreeArtifact (input )) {
377
+ walkTreeInputs (
378
+ baseDirectory , (SpecialArtifact ) input , artifactExpander , childVisitor );
379
+ }
380
+ }
380
381
for (NestedSet <? extends ActionInput > subInputs : someInputFiles .getNonLeaves ()) {
381
382
walkNestedSetInputs (baseDirectory , subInputs , artifactExpander , childVisitor );
382
383
}
383
384
}
384
385
});
385
386
}
386
387
388
+ /** Visits a tree artifact occurring in {@link Spawn#getInputFiles}. */
389
+ private void walkTreeInputs (
390
+ PathFragment baseDirectory ,
391
+ SpecialArtifact tree ,
392
+ ArtifactExpander artifactExpander ,
393
+ InputVisitor visitor )
394
+ throws IOException , ForbiddenActionInputException {
395
+ visitor .visit (
396
+ // Cache key for the sub-mapping containing the files in this tree artifact.
397
+ ImmutableList .of (tree , baseDirectory ),
398
+ new InputWalker () {
399
+ @ Override
400
+ public SortedMap <PathFragment , ActionInput > getLeavesInputMapping () {
401
+ TreeMap <PathFragment , ActionInput > inputMap = new TreeMap <>();
402
+ addInputs (
403
+ inputMap ,
404
+ NestedSetBuilder .create (Order .STABLE_ORDER , tree ),
405
+ artifactExpander ,
406
+ baseDirectory );
407
+ return inputMap ;
408
+ }
409
+ });
410
+ }
411
+
412
+ private static boolean isTreeArtifact (ActionInput input ) {
413
+ return input instanceof SpecialArtifact && ((SpecialArtifact ) input ).isTreeArtifact ();
414
+ }
415
+
387
416
/**
388
417
* Exception signaling that an input was not a regular file: most likely a directory. This
389
418
* exception is currently never thrown in practice since we do not enforce "strict" mode.
390
419
*/
391
420
private static final class ForbiddenNonFileException extends ForbiddenActionInputException {
421
+
392
422
ForbiddenNonFileException (ActionInput input ) {
393
423
super ("Not a file: " + input .getExecPathString ());
394
424
}
0 commit comments