| // Copyright 2019 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.auto.value.AutoValue; | 
 | import com.google.common.collect.ImmutableList; | 
 | import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; | 
 | import com.google.devtools.build.lib.actions.Artifact.ArtifactExpanderImpl; | 
 | import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior; | 
 | import com.google.devtools.build.lib.vfs.Path; | 
 | import com.google.devtools.build.lib.vfs.PathFragment; | 
 | import java.io.IOException; | 
 | import java.util.ArrayList; | 
 | import java.util.Collection; | 
 | import java.util.List; | 
 | import java.util.Map; | 
 | import javax.annotation.Nullable; | 
 |  | 
 | /** | 
 |  * {@link CompletionContext} contains an {@link ArtifactExpander} and {@link ArtifactPathResolver} | 
 |  * used to resolve output files during a {@link | 
 |  * com.google.devtools.build.lib.skyframe.CompletionFunction} evaluation. | 
 |  * | 
 |  * <p>Note that output Artifacts may in fact refer to aggregations, namely tree artifacts and | 
 |  * Filesets. We expand these aggregations when visiting artifacts. | 
 |  */ | 
 | @AutoValue | 
 | public abstract class CompletionContext { | 
 |  | 
 |   public static final CompletionContext FAILED_COMPLETION_CTX = createNull(); | 
 |  | 
 |   public abstract ArtifactExpander expander(); | 
 |  | 
 |   public abstract ArtifactPathResolver pathResolver(); | 
 |  | 
 |   public abstract boolean expandFilesets(); | 
 |  | 
 |   @Nullable | 
 |   public abstract Path execRoot(); | 
 |  | 
 |   public static CompletionContext create( | 
 |       Map<Artifact, Collection<Artifact>> expandedArtifacts, | 
 |       Map<Artifact, ImmutableList<FilesetOutputSymlink>> expandedFilesets, | 
 |       boolean expandFilesets, | 
 |       ActionInputMap inputMap, | 
 |       PathResolverFactory pathResolverFactory, | 
 |       Path execRoot, | 
 |       String workspaceName) | 
 |       throws IOException { | 
 |     ArtifactExpander expander = new ArtifactExpanderImpl(expandedArtifacts, expandedFilesets); | 
 |     ArtifactPathResolver pathResolver = | 
 |         pathResolverFactory.shouldCreatePathResolverForArtifactValues() | 
 |             ? pathResolverFactory.createPathResolverForArtifactValues( | 
 |                 inputMap, expandedArtifacts, expandedFilesets, workspaceName) | 
 |             : ArtifactPathResolver.IDENTITY; | 
 |     return new AutoValue_CompletionContext(expander, pathResolver, expandFilesets, execRoot); | 
 |   } | 
 |  | 
 |   private static CompletionContext createNull() { | 
 |     return new AutoValue_CompletionContext( | 
 |         (artifact, output) -> {}, ArtifactPathResolver.IDENTITY, false, null); | 
 |   } | 
 |  | 
 |   public void visitArtifacts(Iterable<Artifact> artifacts, ArtifactReceiver receiver) { | 
 |     for (Artifact artifact : artifacts) { | 
 |       if (artifact.isMiddlemanArtifact()) { | 
 |         continue; | 
 |       } else if (artifact.isFileset()) { | 
 |         if (expandFilesets()) { | 
 |           visitFileset(artifact, receiver); | 
 |         } | 
 |       } else if (artifact.isTreeArtifact()) { | 
 |         List<Artifact> expandedArtifacts = new ArrayList<>(); | 
 |         expander().expand(artifact, expandedArtifacts); | 
 |         for (Artifact expandedArtifact : expandedArtifacts) { | 
 |           receiver.accept(expandedArtifact); | 
 |         } | 
 |       } else { | 
 |         receiver.accept(artifact); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private void visitFileset(Artifact filesetArtifact, ArtifactReceiver receiver) { | 
 |     ImmutableList<FilesetOutputSymlink> links = expander().getFileset(filesetArtifact); | 
 |     FilesetManifest filesetManifest; | 
 |     try { | 
 |       filesetManifest = | 
 |           FilesetManifest.constructFilesetManifest( | 
 |               links, PathFragment.EMPTY_FRAGMENT, RelativeSymlinkBehavior.RESOLVE); | 
 |     } catch (IOException e) { | 
 |       // Unexpected: RelativeSymlinkBehavior.RESOLVE should not throw. | 
 |       throw new IllegalStateException(e); | 
 |     } | 
 |  | 
 |     for (Map.Entry<PathFragment, String> mapping : filesetManifest.getEntries().entrySet()) { | 
 |       String targetFile = mapping.getValue(); | 
 |       PathFragment locationInFileset = mapping.getKey(); | 
 |       receiver.acceptFilesetMapping( | 
 |           filesetArtifact, locationInFileset, execRoot().getRelative(targetFile)); | 
 |     } | 
 |   } | 
 |  | 
 |   /** A function that accepts an {@link Artifact}. */ | 
 |   public interface ArtifactReceiver { | 
 |     void accept(Artifact artifact); | 
 |  | 
 |     void acceptFilesetMapping(Artifact fileset, PathFragment relName, Path targetFile); | 
 |   } | 
 |  | 
 |   /** A factory for {@link ArtifactPathResolver}. */ | 
 |   public interface PathResolverFactory { | 
 |     ArtifactPathResolver createPathResolverForArtifactValues( | 
 |         ActionInputMap actionInputMap, | 
 |         Map<Artifact, Collection<Artifact>> expandedArtifacts, | 
 |         Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesets, | 
 |         String workspaceName) | 
 |         throws IOException; | 
 |  | 
 |     boolean shouldCreatePathResolverForArtifactValues(); | 
 |   } | 
 | } |