aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java
blob: f7a9e0734a44e8db0f3001489b660dbb9d6ed7e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// Copyright 2014 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.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Preconditions;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * Helper utility to create ActionInput instances.
 */
public final class ActionInputHelper {
  private ActionInputHelper() {
  }

  @VisibleForTesting
  public static ArtifactExpander actionGraphArtifactExpander(
      final ActionGraph actionGraph) {
    return new ArtifactExpander() {
      @Override
      public void expand(Artifact mm, Collection<? super Artifact> output) {
        // Skyframe is stricter in that it checks that "mm" is a input of the action, because
        // it cannot expand arbitrary middlemen without access to a global action graph.
        // We could check this constraint here too, but it seems unnecessary. This code is
        // going away anyway.
        Preconditions.checkArgument(mm.isMiddlemanArtifact(),
            "%s is not a middleman artifact", mm);
        ActionAnalysisMetadata middlemanAction = actionGraph.getGeneratingAction(mm);
        Preconditions.checkState(middlemanAction != null, mm);
        // TODO(bazel-team): Consider expanding recursively or throwing an exception here.
        // Most likely, this code will cause silent errors if we ever have a middleman that
        // contains a middleman.
        if (middlemanAction.getActionType() == Action.MiddlemanType.AGGREGATING_MIDDLEMAN) {
          Artifact.addNonMiddlemanArtifacts(middlemanAction.getInputs(), output,
              Functions.<Artifact>identity());
        }

      }
    };
  }

  /**
   * Most ActionInputs are created and never used again. On the off chance that one is, however, we
   * implement equality via path comparison. Since file caches are keyed by ActionInput, equality
   * checking does come up.
   */
  private static class BasicActionInput implements ActionInput {
    private final String path;

    public BasicActionInput(String path) {
      this.path = Preconditions.checkNotNull(path);
      Preconditions.checkArgument(!path.isEmpty());
    }

    @Override
    public String getExecPathString() {
      return path;
    }

    @Override
    public PathFragment getExecPath() {
      return PathFragment.create(path);
    }

    @Override
    public int hashCode() {
      return path.hashCode();
    }

    @Override
    public boolean equals(Object other) {
      if (this == other) {
        return true;
      }
      if (other == null) {
        return false;
      }
      if (!this.getClass().equals(other.getClass())) {
        return false;
      }
      return this.path.equals(((BasicActionInput) other).path);
    }

    @Override
    public String toString() {
      return "BasicActionInput: " + path;
    }
  }

  /**
   * Creates an ActionInput with just the given relative path and no digest.
   *
   * @param path the relative path of the input.
   * @return a ActionInput.
   */
  public static ActionInput fromPath(String path) {
    return new BasicActionInput(path);
  }

  /**
   * Creates an ActionInput with just the given relative path and no digest.
   *
   * @param path the relative path of the input.
   * @return a ActionInput.
   */
  public static ActionInput fromPath(PathFragment path) {
    return fromPath(path.getPathString());
  }

  /**
   * Creates a sequence of {@link ActionInput}s from a sequence of string paths.
   */
  public static Collection<ActionInput> fromPaths(Collection<String> paths) {
    return Collections2.transform(paths, ActionInputHelper::fromPath);
  }

  /**
   * Instantiates a concrete TreeFileArtifact with the given parent Artifact and path relative to
   * that Artifact.
   */
  public static TreeFileArtifact treeFileArtifact(
      SpecialArtifact parent, PathFragment relativePath) {
    Preconditions.checkState(parent.isTreeArtifact(),
        "Given parent %s must be a TreeArtifact", parent);
    return new TreeFileArtifact(parent, relativePath);
  }

  public static TreeFileArtifact treeFileArtifact(
      SpecialArtifact parent, PathFragment relativePath, ArtifactOwner artifactOwner) {
    Preconditions.checkState(parent.isTreeArtifact(),
        "Given parent %s must be a TreeArtifact", parent);
    return new TreeFileArtifact(
        parent,
        relativePath,
        artifactOwner);
  }

  /**
   * Instantiates a concrete TreeFileArtifact with the given parent Artifact and path relative to
   * that Artifact.
   */
  public static TreeFileArtifact treeFileArtifact(SpecialArtifact parent, String relativePath) {
    return treeFileArtifact(parent, PathFragment.create(relativePath));
  }

  /** Returns an Iterable of TreeFileArtifacts with the given parent and parent relative paths. */
  public static Iterable<TreeFileArtifact> asTreeFileArtifacts(
      final SpecialArtifact parent, Iterable<? extends PathFragment> parentRelativePaths) {
    Preconditions.checkState(parent.isTreeArtifact(),
        "Given parent %s must be a TreeArtifact", parent);
    return Iterables.transform(
        parentRelativePaths, pathFragment -> treeFileArtifact(parent, pathFragment));
  }

  /** Returns a Set of TreeFileArtifacts with the given parent and parent-relative paths. */
  public static Set<TreeFileArtifact> asTreeFileArtifacts(
      final SpecialArtifact parent, Set<? extends PathFragment> parentRelativePaths) {
    Preconditions.checkState(parent.isTreeArtifact(),
        "Given parent %s must be a TreeArtifact", parent);

    ImmutableSet.Builder<TreeFileArtifact> builder = ImmutableSet.builder();
    for (PathFragment path : parentRelativePaths) {
      builder.add(treeFileArtifact(parent, path));
    }

    return builder.build();
  }

  /**
   * Expands middleman artifacts in a sequence of {@link ActionInput}s.
   *
   * <p>Non-middleman artifacts are returned untouched.
   */
  public static List<ActionInput> expandArtifacts(Iterable<? extends ActionInput> inputs,
      ArtifactExpander artifactExpander) {

    List<ActionInput> result = new ArrayList<>();
    List<Artifact> containedArtifacts = new ArrayList<>();
    for (ActionInput input : inputs) {
      if (!(input instanceof Artifact)) {
        result.add(input);
        continue;
      }
      containedArtifacts.add((Artifact) input);
    }
    Artifact.addExpandedArtifacts(containedArtifacts, result, artifactExpander);
    return result;
  }

  /** Formatter for execPath String output. Public because {@link Artifact} uses it directly. */
  public static final Function<ActionInput, String> EXEC_PATH_STRING_FORMATTER =
      ActionInput::getExecPathString;

  public static Iterable<String> toExecPaths(Iterable<? extends ActionInput> artifacts) {
    return Iterables.transform(artifacts, EXEC_PATH_STRING_FORMATTER);
  }
}