blob: 98db892c9ad61dbd7055a4dc5384b57d351eac1c [file] [log] [blame]
// 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.rules.cpp;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.AbstractAction;
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionExecutionException;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Actions;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.Root;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.util.Preconditions;
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;
/**
* Creates mangled symlinks in the solib directory for all shared libraries.
* Libraries that have a potential to contain SONAME field rely on the mangled
* symlink to the parent directory instead.
*
* Such symlinks are used by the linker to ensure that all rpath entries can be
* specified relative to the $ORIGIN.
*/
@Immutable
public final class SolibSymlinkAction extends AbstractAction {
private final Artifact library;
private final Path target;
private final Artifact symlink;
private SolibSymlinkAction(ActionOwner owner, Artifact library, Artifact symlink) {
super(owner, ImmutableList.of(library), ImmutableList.of(symlink));
Preconditions.checkArgument(Link.SHARED_LIBRARY_FILETYPES.matches(library.getFilename()));
this.library = Preconditions.checkNotNull(library);
this.symlink = Preconditions.checkNotNull(symlink);
this.target = library.getPath();
}
@Override
protected void deleteOutputs(Path execRoot) throws IOException {
// Do not delete outputs if action does not intend to do anything.
if (target != null) {
super.deleteOutputs(execRoot);
}
}
@Override
public void execute(
ActionExecutionContext actionExecutionContext) throws ActionExecutionException {
Path mangledPath = symlink.getPath();
try {
FileSystemUtils.createDirectoryAndParents(mangledPath.getParentDirectory());
mangledPath.createSymbolicLink(target);
} catch (IOException e) {
throw new ActionExecutionException("failed to create _solib symbolic link '"
+ symlink.prettyPrint() + "' to target '" + target + "'", e, this, false);
}
}
@Override
public Artifact getPrimaryInput() {
return library;
}
@Override
public Artifact getPrimaryOutput() {
return symlink;
}
@Override
public ResourceSet estimateResourceConsumption(Executor executor) {
return ResourceSet.ZERO;
}
@Override
protected String computeKey() {
Fingerprint f = new Fingerprint();
f.addPath(symlink.getPath());
if (target != null) {
f.addPath(target);
}
return f.hexDigestAndReset();
}
@Override
public String getMnemonic() { return "SolibSymlink"; }
@Override
protected String getRawProgressMessage() { return null; }
/**
* Replaces shared library artifact with mangled symlink and creates related
* symlink action. For artifacts that should retain filename (e.g. libraries
* with SONAME tag), link is created to the parent directory instead.
*
* This action is performed to minimize number of -rpath entries used during
* linking process (by essentially "collecting" as many shared libraries as
* possible in the single directory), since we will be paying quadratic price
* for each additional entry on the -rpath.
*
* @param ruleContext rule context, that requested symlink.
* @param library Shared library artifact that needs to be mangled.
* @param preserveName whether to preserve the name of the library
* @param prefixConsumer whether to prefix the output artifact name with the label of the
* consumer
* @return mangled symlink artifact.
*/
public static Artifact getDynamicLibrarySymlink(final RuleContext ruleContext,
final Artifact library,
boolean preserveName,
boolean prefixConsumer,
BuildConfiguration configuration) {
PathFragment mangledName = getMangledName(
ruleContext, library.getRootRelativePath(), preserveName, prefixConsumer,
configuration.getFragment(CppConfiguration.class));
return getDynamicLibrarySymlinkInternal(
ruleContext, library, mangledName, configuration);
}
/**
* Version of {@link #getDynamicLibrarySymlink} for the special case of C++ runtime libraries.
* These are handled differently than other libraries: neither their names nor directories are
* mangled, i.e. libstdc++.so.6 is symlinked from _solib_[arch]/libstdc++.so.6
*/
public static Artifact getCppRuntimeSymlink(RuleContext ruleContext, Artifact library,
String solibDirOverride, BuildConfiguration configuration) {
PathFragment solibDir = new PathFragment(solibDirOverride != null
? solibDirOverride
: configuration.getFragment(CppConfiguration.class).getSolibDirectory());
PathFragment symlinkName = solibDir.getRelative(library.getRootRelativePath().getBaseName());
return getDynamicLibrarySymlinkInternal(ruleContext, library, symlinkName, configuration);
}
/**
* Internal implementation that takes a pre-determined symlink name; supports both the
* generic {@link #getDynamicLibrarySymlink} and the specialized {@link #getCppRuntimeSymlink}.
*/
private static Artifact getDynamicLibrarySymlinkInternal(RuleContext ruleContext,
Artifact library, PathFragment symlinkName, BuildConfiguration configuration) {
Preconditions.checkArgument(Link.SHARED_LIBRARY_FILETYPES.matches(library.getFilename()));
Preconditions.checkArgument(!library.getRootRelativePath().getSegment(0).startsWith("_solib_"));
// Ignore libraries that are already represented by the symlinks.
Root root = configuration.getBinDirectory(ruleContext.getRule().getRepository());
Artifact symlink = ruleContext.getShareableArtifact(symlinkName, root);
ruleContext.registerAction(
new SolibSymlinkAction(ruleContext.getActionOwner(), library, symlink));
return symlink;
}
/**
* Returns the name of the symlink that will be created for a library, given
* its name.
*
* @param ruleContext rule context that requests symlink
* @param libraryPath the root-relative path of the library
* @param preserveName true if filename should be preserved
* @param prefixConsumer true if the result should be prefixed with the label of the consumer
* @returns root relative path name
*/
public static PathFragment getMangledName(RuleContext ruleContext,
PathFragment libraryPath,
boolean preserveName,
boolean prefixConsumer,
CppConfiguration cppConfiguration) {
String escapedRulePath = Actions.escapedPath(
"_" + ruleContext.getLabel());
String soname = getDynamicLibrarySoname(libraryPath, preserveName);
PathFragment solibDir = new PathFragment(cppConfiguration.getSolibDirectory());
if (preserveName) {
String escapedLibraryPath =
Actions.escapedPath("_" + libraryPath.getParentDirectory().getPathString());
PathFragment mangledDir = solibDir.getRelative(prefixConsumer
? escapedRulePath + "__" + escapedLibraryPath
: escapedLibraryPath);
return mangledDir.getRelative(soname);
} else {
return solibDir.getRelative(prefixConsumer
? escapedRulePath + "__" + soname
: soname);
}
}
/**
* Compute the SONAME to use for a dynamic library. This name is basically the
* name of the shared library in its final symlinked location.
*
* @param libraryPath name of the shared library that needs to be mangled
* @param preserveName true if filename should be preserved, false - mangled
* @return soname to embed in the dynamic library
*/
public static String getDynamicLibrarySoname(PathFragment libraryPath,
boolean preserveName) {
String mangledName;
if (preserveName) {
mangledName = libraryPath.getBaseName();
} else {
mangledName = "lib" + Actions.escapedPath(libraryPath.getPathString());
}
return mangledName;
}
@Override
public boolean shouldReportPathPrefixConflict(ActionAnalysisMetadata action) {
return false; // Always ignore path prefix conflict for the SolibSymlinkAction.
}
}