aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java
diff options
context:
space:
mode:
authorGravatar Michael Staib <mstaib@google.com>2015-04-10 19:08:18 +0000
committerGravatar Lukacs Berki <lberki@google.com>2015-04-13 11:46:55 +0000
commite1861aefd163ccb03e0f34f5706563c5d32e66a5 (patch)
tree29767d0a73b8d72e0eb535d1089a40d613f389fc /src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java
parentbdfdd0905e57ece41bd9f4751a3ed529511cd217 (diff)
Move NativeDepsHelper into the public tree.
-- MOS_MIGRATED_REVID=90828000
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java b/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java
new file mode 100644
index 0000000000..d90357963a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/nativedeps/NativeDepsHelper.java
@@ -0,0 +1,262 @@
+// Copyright 2015 Google Inc. 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.nativedeps;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.Constants;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Root;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.Util;
+import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.rules.cpp.CcLinkParams;
+import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider;
+import com.google.devtools.build.lib.rules.cpp.CppBuildInfo;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
+import com.google.devtools.build.lib.rules.cpp.CppHelper;
+import com.google.devtools.build.lib.rules.cpp.CppLinkAction;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkStaticness;
+import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
+import com.google.devtools.build.lib.rules.cpp.LinkerInputs;
+import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
+import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper class to create a dynamic library for rules which support integration with native code.
+ *
+ * <p>This library gets created by the build system by linking all C++ libraries in the transitive
+ * closure of the dependencies into a standalone dynamic library, with some exceptions. It usually
+ * does not include neverlink libraries or C++ binaries (or their transitive dependencies). Note
+ * that some rules are implicitly neverlink.
+ */
+public abstract class NativeDepsHelper {
+
+ private NativeDepsHelper() {}
+
+ /**
+ * Creates an Action to create a dynamic library by linking all native code
+ * (C/C++) libraries in the transitive dependency closure of a rule.
+ *
+ * <p>This is used for all native code in a rule's dependencies, regardless of type.
+ *
+ * <p>We link the native deps in the equivalent of linkstatic=1, linkshared=1
+ * mode.
+ *
+ * <p>linkstatic=1 means mostly-static mode, i.e. we select the ".a" (or
+ * ".pic.a") files, but we don't include "-static" in linkopts.
+ *
+ * <p>linkshared=1 means we prefer the ".pic.a" files to the ".a" files, and
+ * the LinkTargetType is set to DYNAMIC_LIBRARY which causes Link.java to
+ * include "-shared" in the linker options.
+ *
+ * @param ruleContext the rule context to determine the native library
+ * @param linkParams the {@link CcLinkParams} for the rule, collected with
+ * linkstatic = 1 and linkshared = 1
+ * @param extraLinkOpts additional parameters to the linker
+ * @param includeMalloc whether or not to link in the rule's malloc dependency
+ * @param configuration the {@link BuildConfiguration} to run the link action under
+ * @return the native library runfiles. If the transitive deps closure of
+ * the rule contains no native code libraries, its fields are null.
+ */
+ public static NativeDepsRunfiles maybeCreateNativeDepsAction(final RuleContext ruleContext,
+ CcLinkParams linkParams, Collection<String> extraLinkOpts, boolean includeMalloc,
+ BuildConfiguration configuration) {
+ PathFragment relativePath = Util.getWorkspaceRelativePath(ruleContext.getRule());
+ PathFragment nativeDepsPath = relativePath.replaceName(
+ relativePath.getBaseName() + Constants.NATIVE_DEPS_LIB_SUFFIX + ".so");
+ if (includeMalloc) {
+ // Add in the custom malloc dependency if it was requested.
+ CcLinkParams.Builder linkParamsBuilder = CcLinkParams.builder(true, true);
+ linkParamsBuilder.addTransitiveArgs(linkParams);
+ linkParamsBuilder.addTransitiveTarget(CppHelper.mallocForTarget(ruleContext));
+ linkParams = linkParamsBuilder.build();
+ }
+ return maybeCreateNativeDepsAction(ruleContext, linkParams, extraLinkOpts, configuration,
+ CppHelper.getToolchain(ruleContext), nativeDepsPath,
+ ruleContext.getConfiguration().getBinDirectory());
+ }
+
+ private static final String ANDROID_UNIQUE_DIR = "nativedeps";
+
+ /**
+ * Creates an Action to create a dynamic library for Android by linking all native code (C/C++)
+ * libraries in the transitive dependency closure of a rule.
+ *
+ * <p>We link the native deps in the equivalent of linkstatic=1, linkshared=1 mode.
+ *
+ * <p>linkstatic=1 means mostly-static mode, i.e. we select the ".a" (or ".pic.a") files, but we
+ * don't include "-static" in linkopts.
+ *
+ * <p>linkshared=1 means we prefer the ".pic.a" files to the ".a" files, and the LinkTargetType is
+ * set to DYNAMIC_LIBRARY which causes Link.java to include "-shared" in the linker options.
+ *
+ * @param ruleContext the rule context to determine the native deps library
+ * @param linkParams the {@link CcLinkParams} for the rule, collected with linkstatic = 1 and
+ * linkshared = 1
+ * @return the native deps library runfiles. If the transitive deps closure of the rule contains
+ * no native code libraries, its fields are null.
+ */
+ public static Artifact maybeCreateAndroidNativeDepsAction(final RuleContext ruleContext,
+ CcLinkParams linkParams, BuildConfiguration configuration, CcToolchainProvider toolchain) {
+ PathFragment uniquePath = ruleContext.getUniqueDirectory(ANDROID_UNIQUE_DIR);
+ PathFragment nativeDepsPath =
+ uniquePath.replaceName("lib" + uniquePath.getBaseName() + ".so");
+ return maybeCreateNativeDepsAction(
+ ruleContext, linkParams, /** extraLinkOpts */ ImmutableList.<String>of(),
+ configuration, toolchain, nativeDepsPath, configuration.getBinDirectory()).getLibrary();
+ }
+
+ private static NativeDepsRunfiles maybeCreateNativeDepsAction(final RuleContext ruleContext,
+ CcLinkParams linkParams, Collection<String> extraLinkOpts, BuildConfiguration configuration,
+ CcToolchainProvider toolchain, PathFragment nativeDepsPath, Root bindirIfShared) {
+
+ if (linkParams.getLibraries().isEmpty()) {
+ return NativeDepsRunfiles.EMPTY;
+ }
+
+ List<String> linkopts = new ArrayList<>(extraLinkOpts);
+ linkopts.addAll(linkParams.flattenedLinkopts());
+
+ Map<Artifact, ImmutableList<Artifact>> linkstamps =
+ CppHelper.resolveLinkstamps(ruleContext, linkParams);
+ List<Artifact> buildInfoArtifacts = linkstamps.isEmpty()
+ ? ImmutableList.<Artifact>of()
+ : ruleContext.getAnalysisEnvironment().getBuildInfo(ruleContext, CppBuildInfo.KEY);
+
+ boolean shareNativeDeps = configuration.getFragment(CppConfiguration.class).shareNativeDeps();
+ NestedSet<LibraryToLink> linkerInputs = linkParams.getLibraries();
+ PathFragment linkerOutputPath = shareNativeDeps
+ ? getSharedNativeDepsPath(LinkerInputs.toLibraryArtifacts(linkerInputs),
+ linkopts, linkstamps.keySet(), buildInfoArtifacts,
+ ruleContext.getFeatures())
+ : nativeDepsPath;
+
+ CppLinkAction.Builder builder = new CppLinkAction.Builder(
+ ruleContext, linkerOutputPath, configuration, toolchain);
+ CppLinkAction linkAction = builder
+ .setCrosstoolInputs(toolchain.getLink())
+ .addLibraries(linkerInputs)
+ .setLinkType(LinkTargetType.DYNAMIC_LIBRARY)
+ .setLinkStaticness(LinkStaticness.MOSTLY_STATIC)
+ .addLinkopts(linkopts)
+ .setNativeDeps(true)
+ .setRuntimeInputs(
+ toolchain.getDynamicRuntimeLinkMiddleman(), toolchain.getDynamicRuntimeLinkInputs())
+ .addLinkstamps(linkstamps)
+ .build();
+
+ ruleContext.registerAction(linkAction);
+ final Artifact linkerOutput = linkAction.getPrimaryOutput();
+
+ List<Artifact> runtimeSymlinks = new LinkedList<>();
+
+ if (shareNativeDeps) {
+ // Collect dynamic-linker-resolvable symlinks for C++ runtime library dependencies.
+ // Note we only need these symlinks when --share_native_deps is on, as shared native deps
+ // mangle path names such that the library's conventional _solib RPATH entry
+ // no longer resolves (because the target directory's relative depth gets lost).
+ for (final Artifact runtimeInput : toolchain.getDynamicRuntimeLinkInputs()) {
+ final Artifact runtimeSymlink = ruleContext.getAnalysisEnvironment().getDerivedArtifact(
+ getRuntimeLibraryPath(ruleContext, runtimeInput), bindirIfShared);
+ // Since runtime library symlinks are underneath the target's output directory and
+ // multiple targets may share the same output directory, we need to make sure this
+ // symlink's generating action is only set once.
+ ruleContext.registerAction(
+ new SymlinkAction(ruleContext.getActionOwner(), runtimeInput, runtimeSymlink, null));
+ runtimeSymlinks.add(runtimeSymlink);
+ }
+
+ Artifact symlink = ruleContext.getAnalysisEnvironment().getDerivedArtifact(
+ nativeDepsPath, bindirIfShared);
+ ruleContext.registerAction(
+ new SymlinkAction(ruleContext.getActionOwner(), linkerOutput, symlink, null));
+ return new NativeDepsRunfiles(symlink, runtimeSymlinks);
+ }
+
+ return new NativeDepsRunfiles(linkerOutput, runtimeSymlinks);
+ }
+
+ /**
+ * Returns the path, relative to the runfiles prefix, of the native executable
+ * for the specified rule, i.e. "<package>/<rule><NATIVE_DEPS_LIB_SUFFIX>"
+ */
+ public static PathFragment getExecutablePath(RuleContext ruleContext) {
+ PathFragment relativePath = Util.getWorkspaceRelativePath(ruleContext.getRule());
+ return relativePath.replaceName(relativePath.getBaseName() + Constants.NATIVE_DEPS_LIB_SUFFIX);
+ }
+
+ /**
+ * Returns the path, relative to the runfiles prefix, of a runtime library
+ * symlink for the native library for the specified rule.
+ */
+ private static PathFragment getRuntimeLibraryPath(RuleContext ruleContext, Artifact lib) {
+ PathFragment relativePath = Util.getWorkspaceRelativePath(ruleContext.getRule());
+ PathFragment libParentDir =
+ relativePath.replaceName(lib.getExecPath().getParentDirectory().getBaseName());
+ String libName = lib.getExecPath().getBaseName();
+ return new PathFragment(libParentDir, new PathFragment(libName));
+ }
+
+ /**
+ * Returns the path of the shared native library. The name must be
+ * generated based on the rule-specific inputs to the link actions. At this
+ * point this includes order-sensitive list of linker inputs and options
+ * collected from the transitive closure and linkstamp-related artifacts that
+ * are compiled during linking. All those inputs can be affected by modifying
+ * target attributes (srcs/deps/stamp/etc). However, target build
+ * configuration can be ignored since it will either change output directory
+ * (in case of different configuration instances) or will not affect anything
+ * (if two targets use same configuration). Final goal is for all native
+ * libraries that use identical linker command to use same output name.
+ *
+ * <p>TODO(bazel-team): (2010) Currently process of identifying parameters that can
+ * affect native library name is manual and should be kept in sync with the
+ * code in the CppLinkAction.Builder/CppLinkAction/Link classes which are
+ * responsible for generating linker command line. Ideally we should reuse
+ * generated command line for both purposes - selecting a name of the
+ * native library and using it as link action payload. For now, correctness
+ * of the method below is only ensured by validations in the
+ * CppLinkAction.Builder.build() method.
+ */
+ private static PathFragment getSharedNativeDepsPath(Iterable<Artifact> linkerInputs,
+ Collection<String> linkopts, Iterable<Artifact> linkstamps,
+ Iterable<Artifact> buildInfoArtifacts, Collection<String> features) {
+ Fingerprint fp = new Fingerprint();
+ for (Artifact input : linkerInputs) {
+ fp.addString(input.getExecPathString());
+ }
+ fp.addStrings(linkopts);
+ for (Artifact input : linkstamps) {
+ fp.addString(input.getExecPathString());
+ }
+ for (Artifact input : buildInfoArtifacts) {
+ fp.addString(input.getExecPathString());
+ }
+ for (String feature : features) {
+ fp.addStrings(feature);
+ }
+ return new PathFragment(
+ Constants.NATIVE_DEPS_LIB_SUFFIX + "/" + fp.hexDigestAndReset() + ".so");
+ }
+}