aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java
blob: 2299faee8775a216f9ec214319eaaed34c00e08b (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
// 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.exec;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.actions.AbstractAction;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.BaseSpawn;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.UserExecException;
import com.google.devtools.build.lib.analysis.config.BinTools;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.shell.CommandException;
import com.google.devtools.build.lib.util.CommandBuilder;
import com.google.devtools.build.lib.util.OsUtils;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
import java.util.List;

/**
 * Helper class responsible for the symlink tree creation.
 * Used to generate runfiles and fileset symlink farms.
 */
public final class SymlinkTreeHelper {
  @VisibleForTesting
  public static final String BUILD_RUNFILES = "build-runfiles" + OsUtils.executableExtension();

  /**
   * These actions run faster overall when serialized, because most of their
   * cost is in the ext2 block allocator, and there's less seeking required if
   * their directory creations get non-interleaved allocations. So we give them
   * a huge resource cost.
   */
  public static final ResourceSet RESOURCE_SET = ResourceSet.createWithRamCpuIo(1000, 0.5, 0.75);

  private final Path inputManifest;
  private final Path symlinkTreeRoot;
  private final boolean filesetTree;

  /**
   * Creates SymlinkTreeHelper instance. Can be used independently of
   * SymlinkTreeAction.
   *
   * @param inputManifest exec path to the input runfiles manifest
   * @param symlinkTreeRoot the root of the symlink tree to be created
   * @param filesetTree true if this is fileset symlink tree,
   *                    false if this is a runfiles symlink tree.
   */
  public SymlinkTreeHelper(Path inputManifest, Path symlinkTreeRoot,
      boolean filesetTree) {
    this.inputManifest = inputManifest;
    this.symlinkTreeRoot = symlinkTreeRoot;
    this.filesetTree = filesetTree;
  }

  public Path getOutputManifest() {
    return symlinkTreeRoot;
  }

  /**
   * Creates a symlink tree using a CommandBuilder. This means that the symlink
   * tree will always be present on the developer's workstation. Useful when
   * running commands locally.
   *
   * Warning: this method REALLY executes the command on the box Blaze was
   * run on, without any kind of synchronization, locking, or anything else.
   *
   * @param config the configuration that is used for creating the symlink tree.
   * @throws CommandException
   */
  public void createSymlinksUsingCommand(Path execRoot,
      BuildConfiguration config, BinTools binTools) throws CommandException {
    List<String> argv = getSpawnArgumentList(execRoot, binTools);

    CommandBuilder builder = new CommandBuilder();
    builder.addArgs(argv);
    builder.setWorkingDir(execRoot);
    builder.build().execute();
  }

  /**
   * Creates symlink tree using appropriate method. At this time tree
   * always created using build-runfiles helper application.
   *
   * Note: method may try to acquire resources - meaning that it would
   * block for undetermined period of time. If it is interrupted during
   * that wait, ExecException will be thrown but interrupted bit will be
   * preserved.
   * @param action action instance that requested symlink tree creation
   * @param actionExecutionContext Services that are in the scope of the action.
   * @param enableRunfiles
   */
  public void createSymlinks(
      AbstractAction action,
      ActionExecutionContext actionExecutionContext,
      BinTools binTools,
      ImmutableMap<String, String> shellEnvironment,
      boolean enableRunfiles)
      throws ExecException, InterruptedException {
    if (enableRunfiles) {
      List<String> args =
          getSpawnArgumentList(
              actionExecutionContext.getExecRoot(), binTools);
      actionExecutionContext.getSpawnActionContext(action.getMnemonic()).exec(
          new BaseSpawn.Local(args, shellEnvironment, action, RESOURCE_SET),
          actionExecutionContext);
    } else {
      // Pretend we created the runfiles tree by copying the manifest
      try {
        FileSystemUtils.createDirectoryAndParents(symlinkTreeRoot);
        FileSystemUtils.copyFile(inputManifest, symlinkTreeRoot.getChild("MANIFEST"));
      } catch (IOException e) {
        throw new UserExecException(e.getMessage(), e);
      }
    }
  }

  /**
   * Returns the complete argument list build-runfiles has to be called with.
   */
  private List<String> getSpawnArgumentList(Path execRoot, BinTools binTools) {
    PathFragment path = binTools.getExecPath(BUILD_RUNFILES);
    Preconditions.checkNotNull(path, BUILD_RUNFILES + " not found in embedded tools");

    List<String> args = Lists.newArrayList();
    args.add(path.getPathString());

    if (filesetTree) {
      args.add("--allow_relative");
      args.add("--use_metadata");
    }

    args.add(inputManifest.relativeTo(execRoot).getPathString());
    args.add(symlinkTreeRoot.relativeTo(execRoot).getPathString());

    return args;
  }
}