aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplate.java
blob: db40f23f514ecc49a18e3443a2e4c8bbd9f4aeb5 (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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
// Copyright 2016 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.analysis.actions;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionInputHelper;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.ArtifactOwner;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Map;

/**
 * An {@link ActionTemplate} that expands into {@link SpawnAction}s at execution time.
 */
public final class SpawnActionTemplate implements ActionTemplate<SpawnAction> {
  private final Artifact inputTreeArtifact;
  private final Artifact outputTreeArtifact;
  private final NestedSet<Artifact> commonInputs;
  private final NestedSet<Artifact> allInputs;
  private final NestedSet<Artifact> commonTools;
  private final ActionOwner actionOwner;
  private final String mnemonic;
  private final OutputPathMapper outputPathMapper;
  private final SpawnAction.Builder spawnActionBuilder;
  private final CustomCommandLine commandLineTemplate;

  /**
   * Interface providing mapping between expanded input files under the input TreeArtifact and
   * parent-relative paths of their associated output file under the output TreeArtifact.
   *
   * <p>Users of SpawnActionTemplate must provide a mapper object implementing this interface.
   * SpawnActionTemplate uses the mapper to query for the path of output artifact associated with
   * each input {@link TreeFileArtifact} resolved at execution time.
   */
  public interface OutputPathMapper {
   /**
    * Given the input {@link TreeFileArtifact}, returns the parent-relative path of the associated
    * output {@link TreeFileArtifact}.
    *
    * @param input the input {@link TreeFileArtifact}
    */
    PathFragment parentRelativeOutputPath(TreeFileArtifact input);
  }

  private SpawnActionTemplate(
      ActionOwner actionOwner,
      Artifact inputTreeArtifact,
      Artifact outputTreeArtifact,
      NestedSet<Artifact> commonInputs,
      NestedSet<Artifact> commonTools,
      OutputPathMapper outputPathMapper,
      CustomCommandLine commandLineTemplate,
      String mnemonic,
      SpawnAction.Builder spawnActionBuilder) {
    this.inputTreeArtifact = inputTreeArtifact;
    this.outputTreeArtifact = outputTreeArtifact;
    this.commonTools = commonTools;
    this.commonInputs = NestedSetBuilder.<Artifact>stableOrder()
        .addTransitive(commonInputs)
        .addTransitive(commonTools)
        .build();
    this.allInputs = NestedSetBuilder.<Artifact>stableOrder()
        .add(inputTreeArtifact)
        .addTransitive(this.commonInputs)
        .build();
    this.outputPathMapper = outputPathMapper;
    this.actionOwner = actionOwner;
    this.mnemonic = mnemonic;
    this.spawnActionBuilder = spawnActionBuilder;
    this.commandLineTemplate = commandLineTemplate;
  }

  @Override
  public Iterable<SpawnAction> generateActionForInputArtifacts(
      Iterable<TreeFileArtifact> inputTreeFileArtifacts, ArtifactOwner artifactOwner) {
    ImmutableList.Builder<SpawnAction> expandedActions = new ImmutableList.Builder<>();
    for (TreeFileArtifact inputTreeFileArtifact : inputTreeFileArtifacts) {
      PathFragment parentRelativeOutputPath =
          outputPathMapper.parentRelativeOutputPath(inputTreeFileArtifact);

      TreeFileArtifact outputTreeFileArtifact = ActionInputHelper.treeFileArtifact(
          outputTreeArtifact,
          parentRelativeOutputPath,
          artifactOwner);

      expandedActions.add(createAction(inputTreeFileArtifact, outputTreeFileArtifact));
    }

    return expandedActions.build();
  }

  /**
   * Returns a SpawnAction that takes inputTreeFileArtifact as input and generates
   * outputTreeFileArtifact.
   */
  private SpawnAction createAction(
      TreeFileArtifact inputTreeFileArtifact, TreeFileArtifact outputTreeFileArtifact) {
    SpawnAction.Builder actionBuilder = new SpawnAction.Builder(spawnActionBuilder);
    actionBuilder.addInput(inputTreeFileArtifact);
    actionBuilder.addOutput(outputTreeFileArtifact);

    CommandLine commandLine = commandLineTemplate.evaluateTreeFileArtifacts(
        ImmutableList.of(inputTreeFileArtifact, outputTreeFileArtifact));
    actionBuilder.setCommandLine(commandLine);

    // Note that we pass in nulls below because SpawnActionTemplate does not support param file, and
    // it does not use any default value for executable or shell environment. They must be set
    // explicitly via builder method #setExecutable and #setEnvironment.
    return actionBuilder.buildSpawnAction(
        getOwner(),
        commandLine,
        /*configEnv=*/ null,
        /*defaultShellExecutable=*/ null,
        /*paramsFile=*/ null);
  }

  /**
   * Returns the input TreeArtifact.
   *
   * <p>This method is called by Skyframe to expand the input TreeArtifact into child
   * TreeFileArtifacts. Skyframe then expands this SpawnActionTemplate with the TreeFileArtifacts
   * through {@link #generateActionForInputArtifacts}.
   */
  @Override
  public Artifact getInputTreeArtifact() {
    return inputTreeArtifact;
  }

  /** Returns the output TreeArtifact. */
  @Override
  public Artifact getOutputTreeArtifact() {
    return outputTreeArtifact;
  }

  @Override
  public ActionOwner getOwner() {
    return actionOwner;
  }

  @Override
  public final String getMnemonic() {
    return mnemonic;
  }

  @Override
  public Iterable<Artifact> getTools() {
    return commonTools;
  }

  @Override
  public Iterable<Artifact> getInputs() {
    return allInputs;
  }

  @Override
  public ImmutableSet<Artifact> getOutputs() {
    return ImmutableSet.of(outputTreeArtifact);
  }

  @Override
  public Iterable<Artifact> getMandatoryInputs() {
    return getInputs();
  }

  @Override
  public Iterable<Artifact> getInputFilesForExtraAction(
      ActionExecutionContext actionExecutionContext) {
    return ImmutableList.of();
  }

  @Override
  public ImmutableSet<Artifact> getMandatoryOutputs() {
    return ImmutableSet.of();
  }

  @Override
  public Artifact getPrimaryInput() {
    return inputTreeArtifact;
  }

  @Override
  public Artifact getPrimaryOutput() {
    return outputTreeArtifact;
  }

  @Override
  public Iterable<String> getClientEnvironmentVariables() {
    return spawnActionBuilder
        .buildSpawnAction(getOwner(), CommandLine.of(ImmutableList.of()), null, null, null)
        .getClientEnvironmentVariables();
  }

  @Override
  public boolean shouldReportPathPrefixConflict(ActionAnalysisMetadata action) {
    return this != action;
  }

  @Override
  public MiddlemanType getActionType() {
    return MiddlemanType.NORMAL;
  }

  @Override
  public String prettyPrint() {
    return String.format("action template with output TreeArtifact %s",
        outputTreeArtifact.prettyPrint());
  }

  /** Builder class to construct {@link SpawnActionTemplate} instances. */
  public static class Builder {
    private String actionTemplateMnemonic = "Unknown";
    private OutputPathMapper outputPathMapper;
    private CustomCommandLine commandLineTemplate;
    private PathFragment executable;

    private final Artifact inputTreeArtifact;
    private final Artifact outputTreeArtifact;
    private final NestedSetBuilder<Artifact> inputsBuilder = NestedSetBuilder.stableOrder();
    private final NestedSetBuilder<Artifact> toolsBuilder = NestedSetBuilder.stableOrder();
    private final SpawnAction.Builder spawnActionBuilder;

    /**
     * Creates a {@link SpawnActionTemplate} builder.
     * 
     * @param inputTreeArtifact the required input TreeArtifact.
     * @param outputTreeArtifact the required output TreeArtifact.
     */
    public Builder(Artifact inputTreeArtifact, Artifact outputTreeArtifact) {
      Preconditions.checkState(
          inputTreeArtifact.isTreeArtifact() && outputTreeArtifact.isTreeArtifact(),
          "Either %s or %s is not a TreeArtifact",
          inputTreeArtifact,
          outputTreeArtifact);
      this.inputTreeArtifact = inputTreeArtifact;
      this.outputTreeArtifact = outputTreeArtifact;
      this.spawnActionBuilder = new SpawnAction.Builder();
    }


    /**
     * Sets the mnemonics for both the {@link SpawnActionTemplate} and expanded {@link SpawnAction}.
     */
    public Builder setMnemonics(String actionTemplateMnemonic, String expandedActionMnemonic) {
      this.actionTemplateMnemonic = actionTemplateMnemonic;
      spawnActionBuilder.setMnemonic(expandedActionMnemonic);
      return this;
    }

    /**
     * Adds common tool artifacts. All common tool artifacts will be added as tool artifacts for
     * expanded actions.
     */
    public Builder addCommonTools(Iterable<Artifact> artifacts) {
      toolsBuilder.addAll(artifacts);
      spawnActionBuilder.addTools(artifacts);
      return this;
    }

    /**
     * Adds common tool artifacts. All common tool artifacts will be added as input tool artifacts
     * for expanded actions.
     */
    public Builder addCommonTool(FilesToRunProvider tool) {
      toolsBuilder.addTransitive(tool.getFilesToRun());
      spawnActionBuilder.addTool(tool);
      return this;
    }

    /**
     * Adds common input artifacts. All common input artifacts will be added as input artifacts for
     * expanded actions.
     */
    public Builder addCommonInputs(Iterable<Artifact> artifacts) {
      inputsBuilder.addAll(artifacts);
      spawnActionBuilder.addInputs(artifacts);
      return this;
    }

    /**
     * Adds transitive common input artifacts. All common input artifacts will be added as input
     * artifacts for expanded actions.
     */
    public Builder addCommonTransitiveInputs(NestedSet<Artifact> artifacts) {
      inputsBuilder.addTransitive(artifacts);
      spawnActionBuilder.addTransitiveInputs(artifacts);
      return this;
    }

    /** Sets the map of environment variables for expanded actions. */
    @Deprecated // TODO(ulfjack): Add env variables to the common environment, rather than replacing
    // it wholesale, which ignores --action_env (unless the client code explicitly handles it).
    public Builder setEnvironment(Map<String, String> environment) {
      spawnActionBuilder.setEnvironment(environment);
      return this;
    }

    /**
     * Sets the map of execution info for expanded actions.
     */
    public Builder setExecutionInfo(Map<String, String> executionInfo) {
      spawnActionBuilder.setExecutionInfo(executionInfo);
      return this;
    }

    /**
     * Sets the executable used by expanded actions as a configured target. Automatically adds the
     * files to run to the tools and uses the executable of the target as the executable.
     *
     * <p>Calling this method overrides any previous values set via calls to
     * {@link #setExecutable(Artifact)} and {@link #setExecutable(PathFragment)}.
     */
    public Builder setExecutable(FilesToRunProvider executableProvider) {
      Preconditions.checkArgument(
          executableProvider.getExecutable() != null, "The target does not have an executable");
      spawnActionBuilder.setExecutable(executableProvider);
      addCommonTool(executableProvider);
      this.executable = executableProvider.getExecutable().getExecPath();
      return this;
    }

    /**
     * Sets the executable path used by expanded actions. The path is interpreted relative to the
     * execution root.
     *
     * <p>Calling this method overrides any previous values set via calls to
     * {@link #setExecutable(Artifact)} and {@link #setExecutable(FilesToRunProvider)}.
     */
    public Builder setExecutable(PathFragment executable) {
      spawnActionBuilder.setExecutable(executable);
      this.executable = executable;
      return this;
    }

    /**
     * Sets the executable artifact used by expanded actions. The path is interpreted relative to
     * the execution root.
     *
     * <p>Calling this method overrides any previous values set via calls to
     * {@link #setExecutable(FilesToRunProvider)} and {@link #setExecutable(PathFragment)}.
     */
    public Builder setExecutable(Artifact artifact) {
      spawnActionBuilder.setExecutable(artifact);
      addCommonTools(ImmutableList.of(artifact));
      this.executable = artifact.getExecPath();
      return this;
    }

    /**
     * Sets the command line template used to expand actions.
     */
    public Builder setCommandLineTemplate(CustomCommandLine commandLineTemplate) {
      this.commandLineTemplate = commandLineTemplate;
      return this;
    }

    /**
     * Sets the {@link OutputPathMapper} object used to get the parent-relative paths of output
     * {@link TreeFileArtifact}.
     */
    public Builder setOutputPathMapper(OutputPathMapper outputPathMapper) {
      this.outputPathMapper = outputPathMapper;
      return this;
    }

    /**
     * Builds and returns the {@link SpawnActionTemplate} using the accumulated builder information.
     *
     * @param actionOwner the action owner of the SpawnActionTemplate to be built.
     */
    public SpawnActionTemplate build(ActionOwner actionOwner) {
      Preconditions.checkNotNull(executable);

      return new SpawnActionTemplate(
          actionOwner,
          Preconditions.checkNotNull(inputTreeArtifact),
          Preconditions.checkNotNull(outputTreeArtifact),
          inputsBuilder.build(),
          toolsBuilder.build(),
          Preconditions.checkNotNull(outputPathMapper),
          Preconditions.checkNotNull(commandLineTemplate),
          actionTemplateMnemonic,
          spawnActionBuilder);
    }
  }
}