blob: 0062118134f731c4827e965e5e7f84a6463c21bd [file]
// Copyright 2026 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.actions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import java.util.Iterator;
import java.util.List;
/**
* Efficient representation of inputs to a {@link Spawn}, supporting the lazy union of multiple
* nested sets and lists.
*
* <p>A common pattern for spawns is to take {@link Action#getInputs} and add a few additional files
* such as parameter files. Creating a new {@link NestedSet} instance to include a few additional
* files is suboptimal: it doesn't share a cached list representation with {@link Action#getInputs},
* and it isn't expected to be fed into a larger {@link NestedSet}.
*
* <p>Calling {@link #flatten} calls {@link NestedSet#toList} on an <em>existing</em> {@link
* NestedSet} (maximizing the likelihood of a cached representation), and lazily concatenates the
* additional files using {@link Iterables#concat}.
*/
public final class SpawnInputs {
private static final SpawnInputs EMPTY =
new SpawnInputs(
NestedSetBuilder.emptySet(Order.STABLE_ORDER),
NestedSetBuilder.emptySet(Order.STABLE_ORDER),
ImmutableList.of(),
/* sizeOfRest= */ 0);
/** Returns a canonical empty {@link SpawnInputs} instance. */
public static SpawnInputs empty() {
return EMPTY;
}
/** Creates {@link SpawnInputs} from a single {@link NestedSet}. */
public static SpawnInputs of(NestedSet<? extends ActionInput> nestedSet) {
return of(nestedSet, ImmutableList.of());
}
/** Creates {@link SpawnInputs} from a {@link NestedSet} plus some additional inputs. */
public static SpawnInputs of(
NestedSet<? extends ActionInput> nestedSet, List<? extends ActionInput> list) {
return of(nestedSet, NestedSetBuilder.emptySet(Order.STABLE_ORDER), list);
}
/**
* Creates {@link SpawnInputs} from two nested sets plus some additional inputs.
*
* <p>This is useful for input-discovering actions to union mandatory inputs and discovered
* inputs.
*/
public static SpawnInputs of(
NestedSet<? extends ActionInput> nestedSet1,
NestedSet<? extends ActionInput> nestedSet2,
List<? extends ActionInput> list) {
return new SpawnInputs(nestedSet1, nestedSet2, list, list.size());
}
private final NestedSet<? extends ActionInput> subset1;
private final NestedSet<? extends ActionInput> subset2;
private final Iterable<? extends ActionInput> rest;
private final int sizeOfRest;
private SpawnInputs(
NestedSet<? extends ActionInput> subset1,
NestedSet<? extends ActionInput> subset2,
Iterable<? extends ActionInput> rest,
int sizeOfRest) {
this.subset1 = subset1;
this.subset2 = subset2;
this.rest = rest;
this.sizeOfRest = sizeOfRest;
}
/**
* Returns a single {@link NestedSet} containing all of the spawn's inputs.
*
* <p>Prefer {@link #flatten} for iterating over the inputs, as it is more likely to reuse cached
* list representations. This method should only be used by callers that need to decompose the
* {@link NestedSet} structurally.
*/
public NestedSet<ActionInput> asNestedSet() {
return NestedSetBuilder.<ActionInput>fromNestedSet(subset1)
.addTransitive(subset2)
.addAll(rest)
.build();
}
/**
* Flattens contained nested sets and returns a {@link FlattenedInputs} suitable for iteration.
*
* <p>Note that the returned {@link FlattenedInputs} may contain duplicate inputs if the
* underlying sets or lists contain duplicates.
*/
public FlattenedInputs flatten() {
return new FlattenedInputs(subset1.toList(), subset2.toList(), rest, sizeOfRest);
}
/**
* Returns a {@link SpawnInputs} with all the inputs in this instance plus the given additional
* inputs.
*
* <p>Does not mutate this instance.
*/
public SpawnInputs plus(List<ActionInput> additional) {
if (additional.isEmpty()) {
return this;
}
return new SpawnInputs(
subset1, subset2, Iterables.concat(rest, additional), sizeOfRest + additional.size());
}
/**
* Flattened representation of {@link SpawnInputs} that is ready for iteration.
*
* <p>This iterable may contain duplicate inputs.
*/
public static final class FlattenedInputs implements Iterable<ActionInput> {
private final List<? extends ActionInput> list1;
private final List<? extends ActionInput> list2;
private final Iterable<? extends ActionInput> rest;
private final int sizeOfRest;
private FlattenedInputs(
List<? extends ActionInput> list1,
List<? extends ActionInput> list2,
Iterable<? extends ActionInput> rest,
int sizeOfRest) {
this.list1 = list1;
this.list2 = list2;
this.rest = rest;
this.sizeOfRest = sizeOfRest;
}
/** Returns the number of inputs. */
public int size() {
return list1.size() + list2.size() + sizeOfRest;
}
/** Returns true if there are no inputs. */
public boolean isEmpty() {
return list1.isEmpty() && list2.isEmpty() && sizeOfRest == 0;
}
@Override
public Iterator<ActionInput> iterator() {
return Iterators.concat(list1.iterator(), list2.iterator(), rest.iterator());
}
}
}