-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce FileOpNodeMemoizingLookup for tracking file dependencies.
This change introduces `FileOpNodeMemoizingLookup`, a new class responsible for accurately determining the file dependencies of both analysis (configured target) and execution (action) SkyValues. **Key Changes:** * **FileOpNodeMemoizingLookup:** This new class efficiently computes and caches file dependencies by performing a concurrent, memoized, and recursive traversal of Skyframe edges. It distinguishes between analysis and execution file dependencies, storing them separately within `FileOpNode` (previously `FileSystemOperationNode`). * **Refined `AbstractNestedFileOpNodes`:** The `AbstractNestedFileOpNodes` class (which is used to store a set of `FileOpNode`s) is now split into two subclasses: * `NestedFileOpNodes`: Represents a set of analysis file dependencies *without* any source file dependencies. * `NestedFileOpNodesWithSources`: Represents a set of analysis file dependencies that *includes* immediate source file dependencies. This separation allows a more precise representation of dependencies, distinguishing between files depended on during analysis and those depended on during execution. Source dependencies, which correspond to `InputFileConfiguredTarget` instances, are declared during analysis but are only truly depended upon by the actions owned by the corresponding configured targets, not the configured targets themselves. This distinction optimizes storage by allowing both configured targets and actions to share the same underlying file dependency graph structure. * **Consolidated `FileOpNodeOrFuture`:** The `FileSystemOperationNode` class (and related classes) have been renamed to `FileOpNode` and moved into the newly added `FileOpNodeOrFuture` interface. This consolidation improves code readability by centralizing the hierarchical structure of file operation nodes. * **Renamed `FileOpNode`:** The old name, `FileSystemOperationNode`, was becoming cumbersome. The new name, `FileOpNode`, is more concise. PiperOrigin-RevId: 717556933 Change-Id: I7376f1d5ffb30e520af0e15140d00159f2a78d6d
- Loading branch information
1 parent
04a44fd
commit b704fdb
Showing
15 changed files
with
830 additions
and
135 deletions.
There are no files selected for viewing
155 changes: 155 additions & 0 deletions
155
src/main/java/com/google/devtools/build/lib/skyframe/AbstractNestedFileOpNodes.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// Copyright 2025 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.skyframe; | ||
|
||
import static com.google.common.base.Preconditions.checkArgument; | ||
import static com.google.devtools.build.lib.skyframe.FileOpNodeOrFuture.EmptyFileOpNode.EMPTY_FILE_OP_NODE; | ||
|
||
import com.google.devtools.build.lib.skyframe.FileOpNodeOrFuture.FileOpNode; | ||
import com.google.devtools.build.lib.skyframe.FileOpNodeOrFuture.FileOpNodeOrEmpty; | ||
import java.util.Collection; | ||
import javax.annotation.Nullable; | ||
|
||
/** | ||
* Represents a collection of {@link FileOpNode}s, allowing for nested structures to represent | ||
* complex file dependencies. | ||
* | ||
* <p>This class serves as a container for multiple {@link FileOpNode} instances, enabling the | ||
* representation of file operation dependencies in a hierarchical manner. It differentiates between | ||
* analysis dependencies (for example, BUILD and .bzl files) and "source" dependencies, used during | ||
* execution (for example, .cpp, .h or .java files). It keeps them together to optimize storage. | ||
* | ||
* <p><b>Source vs. Analysis Dependencies:</b> | ||
* | ||
* <ul> | ||
* <li><b>Analysis:</b> During the analysis phase, source files are declared, but configured | ||
* targets (which define actions) do not depend on the <i>contents</i> of these source files, | ||
* for example, .cpp, .h or .java files. | ||
* <li><b>Execution:</b> The execution phase creates actual dependencies on the contents of source | ||
* files as actions are run. | ||
* </ul> | ||
* | ||
* <p><b>Why combine them?</b> <br> | ||
* Logically, source and analysis dependencies could be tracked separately with different {@link | ||
* FileOpNode}s. However, this would duplicate the dependency graph structure in persistent storage, | ||
* which is expensive. This class keeps them together, trading off a bit of complexity for reduced | ||
* storage overhead. The structure is written only once, and the interpretation of dependencies must | ||
* be handled by the client. | ||
* | ||
* <p><b>Subclasses:</b> | ||
* | ||
* <ul> | ||
* <li>{@link NestedFileOpNodes}: Represents a set of {@link FileOpNode}s without any immediate | ||
* source file dependencies. | ||
* <li>{@link NestedFileOpNodesWithSources}: Represents a set of {@link FileOpNode}s along with a | ||
* list of immediate source file dependencies ({@link FileKey}s). | ||
* </ul> | ||
*/ | ||
public abstract sealed class AbstractNestedFileOpNodes implements FileOpNodeOrFuture.FileOpNode | ||
permits AbstractNestedFileOpNodes.NestedFileOpNodes, | ||
AbstractNestedFileOpNodes.NestedFileOpNodesWithSources { | ||
private final FileOpNode[] analysisDependencies; | ||
|
||
/** | ||
* Opaque storage for use by serialization. | ||
* | ||
* <p>{@link FileOpNode}, {@link FileKey} and {@link DirectoryListingKey} are mutually dependent | ||
* via {@link FileOpNode}. This type is opaque to avoid forcing {@link FileKey} and {@link | ||
* DirectoryListingKey} to depend on serialization implementation code. | ||
* | ||
* <p>The serialization implementation initializes this field with double-checked locking so it is | ||
* marked volatile. | ||
*/ | ||
private volatile Object serializationScratch; | ||
|
||
/** | ||
* Effectively, a factory method for {@link NestedFileOpNodes}, but formally a factory method for | ||
* {@link FileOpNodeOrEmpty}. | ||
* | ||
* <p>Returns {@link EMPTY_FILE_OP_NODE} if {@code analysisDependencies} is empty. When {@code | ||
* analysisDependencies} contains only one node, returns the node directly instead of wrapping it. | ||
* Otherwise, returns a {@link NestedFileOpNodes} instance wrapping {@code analysisDependencies}. | ||
*/ | ||
public static FileOpNodeOrEmpty from(Collection<FileOpNode> analysisDependencies) { | ||
if (analysisDependencies.isEmpty()) { | ||
return EMPTY_FILE_OP_NODE; | ||
} | ||
if (analysisDependencies.size() == 1) { | ||
return analysisDependencies.iterator().next(); | ||
} | ||
return new NestedFileOpNodes(analysisDependencies.toArray(FileOpNode[]::new)); | ||
} | ||
|
||
/** | ||
* Creates {@link NestedFileOpNodesWithSources} with reductions similar to {@link | ||
* #from(Collection<FileOpNode>)}. | ||
*/ | ||
public static FileOpNodeOrEmpty from( | ||
Collection<FileOpNode> analysisDependencies, Collection<FileKey> sources) { | ||
if (sources.isEmpty()) { | ||
return from(analysisDependencies); | ||
} | ||
// It's unclear if `analysisDependencies` can ever be empty here in practice, but it's | ||
// permitted. It should be rare enough that defining a special type for it isn't worth it. | ||
return new NestedFileOpNodesWithSources( | ||
analysisDependencies.toArray(FileOpNode[]::new), sources.toArray(FileKey[]::new)); | ||
} | ||
|
||
private AbstractNestedFileOpNodes(FileOpNode[] analysisDependencies) { | ||
this.analysisDependencies = analysisDependencies; | ||
} | ||
|
||
public int analysisDependenciesCount() { | ||
return analysisDependencies.length; | ||
} | ||
|
||
public FileOpNode getAnalysisDependency(int index) { | ||
return analysisDependencies[index]; | ||
} | ||
|
||
@Nullable | ||
public Object getSerializationScratch() { | ||
return serializationScratch; | ||
} | ||
|
||
public void setSerializationScratch(Object value) { | ||
this.serializationScratch = value; | ||
} | ||
|
||
/** A set of {@link FileOpNode}s with no immediate source dependencies. */ | ||
public static final class NestedFileOpNodes extends AbstractNestedFileOpNodes { | ||
private NestedFileOpNodes(FileOpNode[] analysisDependencies) { | ||
super(analysisDependencies); | ||
checkArgument(analysisDependencies.length > 0); | ||
} | ||
} | ||
|
||
/** A set of analysis and source file dependencies. */ | ||
public static final class NestedFileOpNodesWithSources extends AbstractNestedFileOpNodes { | ||
private final FileKey[] sources; // never empty | ||
|
||
private NestedFileOpNodesWithSources(FileOpNode[] nodes, FileKey[] sources) { | ||
super(nodes); | ||
this.sources = sources; | ||
} | ||
|
||
public int sourceCount() { | ||
return sources.length; | ||
} | ||
|
||
public FileKey getSource(int i) { | ||
return sources[i]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
src/main/java/com/google/devtools/build/lib/skyframe/FileOpNodeOrFuture.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2024 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.skyframe; | ||
|
||
import com.google.devtools.build.lib.concurrent.SettableFutureKeyedValue; | ||
import com.google.devtools.build.skyframe.SkyKey; | ||
import java.util.function.BiConsumer; | ||
|
||
/** | ||
* A possibly empty nested set of file system operations. | ||
* | ||
* <p>This value represents the set of file system operation dependencies of a given Skyframe entry, | ||
* computed by Skyframe graph traversal. | ||
*/ | ||
@SuppressWarnings("InterfaceWithOnlyStatics") // sealed hierarchy root | ||
public sealed interface FileOpNodeOrFuture | ||
permits FileOpNodeOrFuture.FileOpNodeOrEmpty, FileOpNodeOrFuture.FutureFileOpNode { | ||
|
||
/** A possibly empty set of file system dependencies. */ | ||
sealed interface FileOpNodeOrEmpty extends FileOpNodeOrFuture | ||
permits EmptyFileOpNode, FileOpNode {} | ||
|
||
/** A non-empty set of filesystem operations. */ | ||
sealed interface FileOpNode extends FileOpNodeOrEmpty | ||
permits FileKey, DirectoryListingKey, AbstractNestedFileOpNodes {} | ||
|
||
/** Empty set of filesystem dependencies. */ | ||
enum EmptyFileOpNode implements FileOpNodeOrEmpty { | ||
EMPTY_FILE_OP_NODE; | ||
} | ||
|
||
/** The in-flight computation of a {@link FileOpNodeOrEmpty}. */ | ||
static final class FutureFileOpNode | ||
extends SettableFutureKeyedValue<FutureFileOpNode, SkyKey, FileOpNodeOrEmpty> | ||
implements FileOpNodeOrFuture { | ||
public FutureFileOpNode(SkyKey key, BiConsumer<SkyKey, FileOpNodeOrEmpty> consumer) { | ||
super(key, consumer); | ||
} | ||
} | ||
} |
22 changes: 0 additions & 22 deletions
22
src/main/java/com/google/devtools/build/lib/skyframe/FileSystemOperationNode.java
This file was deleted.
Oops, something went wrong.
72 changes: 0 additions & 72 deletions
72
src/main/java/com/google/devtools/build/lib/skyframe/NestedFileSystemOperationNodes.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.