// 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.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.ResourceManager; import com.google.devtools.build.lib.actions.ResourceManager.ResourceHandle; import com.google.devtools.build.lib.actions.ResourceSet; 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.OS; import com.google.devtools.build.lib.util.OsUtils; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.UnixFileSystem; import com.google.devtools.build.lib.vfs.UnixFileSystem.SymlinkStrategy; import com.google.devtools.build.lib.vfs.WindowsFileSystem; import java.util.List; /** * Helper class responsible for the symlink tree creation. * Used to generate runfiles and fileset symlink farms. */ public final class SymlinkTreeHelper { private 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 PathFragment inputManifest; private final PathFragment 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 exec path to the symlink tree location * @param filesetTree true if this is fileset symlink tree, * false if this is a runfiles symlink tree. */ public SymlinkTreeHelper(PathFragment inputManifest, PathFragment symlinkTreeRoot, boolean filesetTree) { this.inputManifest = inputManifest; this.symlinkTreeRoot = symlinkTreeRoot; this.filesetTree = filesetTree; } public PathFragment getSymlinkTreeRoot() { 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 argv = getSpawnArgumentList(execRoot, binTools, config.getShExecutable()); 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 shExecutable */ public void createSymlinks( AbstractAction action, ActionExecutionContext actionExecutionContext, BinTools binTools, PathFragment shExecutable, ImmutableMap shellEnvironment) throws ExecException, InterruptedException { List args = getSpawnArgumentList( actionExecutionContext.getExecutor().getExecRoot(), binTools, shExecutable); try (ResourceHandle handle = ResourceManager.instance().acquireResources(action, RESOURCE_SET)) { actionExecutionContext.getExecutor().getSpawnActionContext(action.getMnemonic()).exec( new BaseSpawn.Local(args, shellEnvironment, action), actionExecutionContext); } } /** * Returns the complete argument list build-runfiles has to be called with. */ private List getSpawnArgumentList( Path execRoot, BinTools binTools, PathFragment shExecutable) { PathFragment path = binTools.getExecPath(BUILD_RUNFILES); Preconditions.checkNotNull(path, BUILD_RUNFILES + " not found in embedded tools"); List args = Lists.newArrayList(); if (OS.getCurrent() == OS.WINDOWS) { // During bootstrapping, build-runfiles is a shell script, that cannot be directly // executed on Windows, so we shell out. args.add(shExecutable.getPathString()); args.add("-c"); args.add("$0 $*"); } args.add(execRoot.getRelative(path).getPathString()); if (filesetTree) { args.add("--allow_relative"); args.add("--use_metadata"); } FileSystem fs = execRoot.getFileSystem(); if ((fs instanceof WindowsFileSystem) || (fs instanceof UnixFileSystem && ((UnixFileSystem) fs).getSymlinkStrategy() == SymlinkStrategy.WINDOWS_COMPATIBLE)) { args.add("--windows_compatible"); } args.add(inputManifest.getPathString()); args.add(symlinkTreeRoot.getPathString()); return args; } }