/*
* Copyright 2015-present Facebook, Inc.
*
* 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.facebook.buck.cxx;
import com.facebook.buck.graph.AbstractBreadthFirstTraversal;
import com.facebook.buck.graph.DirectedAcyclicGraph;
import com.facebook.buck.graph.MutableDirectedGraph;
import com.facebook.buck.graph.TopologicalSort;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultBuildTargetSourcePath;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.args.Arg;
import com.facebook.buck.rules.args.SourcePathArg;
import com.facebook.buck.rules.args.StringArg;
import com.facebook.buck.util.immutables.BuckStyleImmutable;
import com.google.common.base.Functions;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.immutables.value.Value;
public class Omnibus {
private static final Flavor OMNIBUS_FLAVOR = InternalFlavor.of("omnibus");
private static final Flavor DUMMY_OMNIBUS_FLAVOR = InternalFlavor.of("dummy-omnibus");
private Omnibus() {}
private static String getOmnibusSoname(CxxPlatform cxxPlatform) {
return String.format("libomnibus.%s", cxxPlatform.getSharedLibraryExtension());
}
private static BuildTarget getRootTarget(BuildTarget base, BuildTarget root) {
return BuildTarget.builder(base)
.addFlavors(InternalFlavor.of(Flavor.replaceInvalidCharacters(root.toString())))
.build();
}
private static BuildTarget getDummyRootTarget(BuildTarget root) {
return root.withAppendedFlavors(InternalFlavor.of("dummy"));
}
private static boolean shouldCreateDummyRoot(NativeLinkTarget target, CxxPlatform cxxPlatform) {
return target.getNativeLinkTargetMode(cxxPlatform).getType() == Linker.LinkType.EXECUTABLE;
}
private static Iterable<NativeLinkable> getDeps(
NativeLinkable nativeLinkable, CxxPlatform cxxPlatform) {
return Iterables.concat(
nativeLinkable.getNativeLinkableDepsForPlatform(cxxPlatform),
nativeLinkable.getNativeLinkableExportedDepsForPlatform(cxxPlatform));
}
// Returned the dependencies for the given node, which can either be a `NativeLinkable` or a
// `NativeLinkTarget`.
private static Iterable<? extends NativeLinkable> getDeps(
BuildTarget target,
Map<BuildTarget, ? extends NativeLinkTarget> nativeLinkTargets,
Map<BuildTarget, ? extends NativeLinkable> nativeLinkables,
CxxPlatform cxxPlatform) {
if (nativeLinkables.containsKey(target)) {
NativeLinkable nativeLinkable = Preconditions.checkNotNull(nativeLinkables.get(target));
return getDeps(nativeLinkable, cxxPlatform);
} else {
NativeLinkTarget nativeLinkTarget = Preconditions.checkNotNull(nativeLinkTargets.get(target));
return nativeLinkTarget.getNativeLinkTargetDeps(cxxPlatform);
}
}
// Build the data structure containing bookkeeping which describing the omnibus link for the
// given included and excluded roots.
protected static OmnibusSpec buildSpec(
final CxxPlatform cxxPlatform,
final Iterable<? extends NativeLinkTarget> includedRoots,
final Iterable<? extends NativeLinkable> excludedRoots) {
// A map of targets to native linkable objects. We maintain this, so that we index our
// bookkeeping around `BuildTarget` and avoid having to guarantee that all other types are
// hashable.
final Map<BuildTarget, NativeLinkable> nativeLinkables = new LinkedHashMap<>();
// The nodes which should *not* be included in the omnibus link.
final Set<BuildTarget> excluded = new LinkedHashSet<>();
// Process all the roots included in the omnibus link.
final Map<BuildTarget, NativeLinkTarget> roots = new LinkedHashMap<>();
Map<BuildTarget, NativeLinkable> rootDeps = new LinkedHashMap<>();
for (NativeLinkTarget root : includedRoots) {
roots.put(root.getBuildTarget(), root);
for (NativeLinkable dep :
NativeLinkables.getNativeLinkables(
cxxPlatform,
root.getNativeLinkTargetDeps(cxxPlatform),
Linker.LinkableDepType.SHARED)
.values()) {
Linker.LinkableDepType linkStyle =
NativeLinkables.getLinkStyle(
dep.getPreferredLinkage(cxxPlatform), Linker.LinkableDepType.SHARED);
Preconditions.checkState(linkStyle != Linker.LinkableDepType.STATIC);
// We only consider deps which aren't *only* statically linked.
if (linkStyle == Linker.LinkableDepType.SHARED) {
rootDeps.put(dep.getBuildTarget(), dep);
nativeLinkables.put(dep.getBuildTarget(), dep);
}
}
}
// Process all roots excluded from the omnibus link, and add them to our running list of
// excluded nodes.
for (NativeLinkable root : excludedRoots) {
nativeLinkables.put(root.getBuildTarget(), root);
excluded.add(root.getBuildTarget());
}
// Perform the first walk starting from the native linkable nodes immediately reachable via the
// included roots. We'll accomplish two things here:
// 1. Build up the map of node names to their native linkable objects.
// 2. Perform an initial discovery of dependency nodes to exclude from the omnibus link.
new AbstractBreadthFirstTraversal<BuildTarget>(rootDeps.keySet()) {
@Override
public ImmutableSet<BuildTarget> visit(BuildTarget target) {
NativeLinkable nativeLinkable = Preconditions.checkNotNull(nativeLinkables.get(target));
ImmutableMap<BuildTarget, NativeLinkable> deps =
Maps.uniqueIndex(getDeps(nativeLinkable, cxxPlatform), NativeLinkable::getBuildTarget);
nativeLinkables.putAll(deps);
if (nativeLinkable.getPreferredLinkage(cxxPlatform) == NativeLinkable.Linkage.SHARED) {
excluded.add(target);
}
return deps.keySet();
}
}.start();
// Do another walk to flesh out the transitively excluded nodes.
new AbstractBreadthFirstTraversal<BuildTarget>(excluded) {
@Override
public Iterable<BuildTarget> visit(BuildTarget target) {
NativeLinkable nativeLinkable = Preconditions.checkNotNull(nativeLinkables.get(target));
ImmutableMap<BuildTarget, NativeLinkable> deps =
Maps.uniqueIndex(getDeps(nativeLinkable, cxxPlatform), NativeLinkable::getBuildTarget);
nativeLinkables.putAll(deps);
excluded.add(target);
return deps.keySet();
}
}.start();
// And then we can do one last walk to create the actual graph which contain only root and body
// nodes to include in the omnibus link.
final MutableDirectedGraph<BuildTarget> graphBuilder = new MutableDirectedGraph<>();
final Set<BuildTarget> deps = new LinkedHashSet<>();
new AbstractBreadthFirstTraversal<BuildTarget>(Sets.difference(rootDeps.keySet(), excluded)) {
@Override
public Iterable<BuildTarget> visit(BuildTarget target) {
graphBuilder.addNode(target);
Set<BuildTarget> keep = new LinkedHashSet<>();
for (BuildTarget dep :
Iterables.transform(
getDeps(target, roots, nativeLinkables, cxxPlatform),
NativeLinkable::getBuildTarget)) {
if (excluded.contains(dep)) {
deps.add(dep);
} else {
keep.add(dep);
graphBuilder.addEdge(target, dep);
}
}
return keep;
}
}.start();
DirectedAcyclicGraph<BuildTarget> graph = new DirectedAcyclicGraph<>(graphBuilder);
// Since we add all undefined root symbols into the omnibus library, we also need to include
// any excluded root deps as deps of omnibus, as they may fulfill these undefined symbols.
// Also add any excluded nodes that are also root dependencies.
deps.addAll(Sets.intersection(rootDeps.keySet(), excluded));
return ImmutableOmnibusSpec.builder()
.graph(graph)
.roots(roots)
.body(
FluentIterable.from(graph.getNodes())
.filter(Predicates.not(roots.keySet()::contains))
.toMap(Functions.forMap(nativeLinkables)))
.deps(Maps.asMap(deps, Functions.forMap(nativeLinkables)))
.excluded(Maps.asMap(excluded, Functions.forMap(nativeLinkables)))
.build();
}
// Build a dummy library with the omnibus SONAME. We'll need this to break any dep cycle between
// the omnibus roots and the merged omnibus body, by first linking the roots against this
// dummy lib (ignoring missing symbols), then linking the omnibus body with the roots.
private static SourcePath createDummyOmnibus(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathRuleFinder ruleFinder,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
ImmutableList<? extends Arg> extraLdflags) {
BuildTarget dummyOmnibusTarget =
params.getBuildTarget().withAppendedFlavors(DUMMY_OMNIBUS_FLAVOR);
String omnibusSoname = getOmnibusSoname(cxxPlatform);
CxxLink rule =
ruleResolver.addToIndex(
CxxLinkableEnhancer.createCxxLinkableSharedBuildRule(
cxxBuckConfig,
cxxPlatform,
params,
ruleResolver,
ruleFinder,
dummyOmnibusTarget,
BuildTargets.getGenPath(params.getProjectFilesystem(), dummyOmnibusTarget, "%s")
.resolve(omnibusSoname),
Optional.of(omnibusSoname),
extraLdflags));
return rule.getSourcePathToOutput();
}
// Create a build rule which links the given root node against the merged omnibus library
// described by the given spec file.
protected static OmnibusRoot createRoot(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathRuleFinder ruleFinder,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
ImmutableList<? extends Arg> extraLdflags,
OmnibusSpec spec,
SourcePath omnibus,
NativeLinkTarget root,
BuildTarget rootTargetBase,
Optional<Path> output)
throws NoSuchBuildTargetException {
ImmutableList.Builder<Arg> argsBuilder = ImmutableList.builder();
// Add any extra flags to the link.
argsBuilder.addAll(extraLdflags);
// Since the dummy omnibus library doesn't actually contain any symbols, make sure the linker
// won't drop its runtime reference to it.
argsBuilder.addAll(
StringArg.from(cxxPlatform.getLd().resolve(ruleResolver).getNoAsNeededSharedLibsFlags()));
// Since we're linking against a dummy libomnibus, ignore undefined symbols.
argsBuilder.addAll(
StringArg.from(cxxPlatform.getLd().resolve(ruleResolver).getIgnoreUndefinedSymbolsFlags()));
// Add the args for the root link target first.
NativeLinkableInput input = root.getNativeLinkTargetInput(cxxPlatform);
argsBuilder.addAll(input.getArgs());
// Grab a topologically sorted mapping of all the root's deps.
ImmutableMap<BuildTarget, NativeLinkable> deps =
NativeLinkables.getNativeLinkables(
cxxPlatform, root.getNativeLinkTargetDeps(cxxPlatform), Linker.LinkableDepType.SHARED);
// Now process the dependencies in topological order, to assemble the link line.
boolean alreadyAddedOmnibusToArgs = false;
for (Map.Entry<BuildTarget, NativeLinkable> entry : deps.entrySet()) {
BuildTarget target = entry.getKey();
NativeLinkable nativeLinkable = entry.getValue();
Linker.LinkableDepType linkStyle =
NativeLinkables.getLinkStyle(
nativeLinkable.getPreferredLinkage(cxxPlatform), Linker.LinkableDepType.SHARED);
// If this dep needs to be linked statically, then we always link it directly.
if (linkStyle != Linker.LinkableDepType.SHARED) {
Preconditions.checkState(linkStyle == Linker.LinkableDepType.STATIC_PIC);
argsBuilder.addAll(nativeLinkable.getNativeLinkableInput(cxxPlatform, linkStyle).getArgs());
continue;
}
// If this dep is another root node, substitute in the custom linked library we built for it.
if (spec.getRoots().containsKey(target)) {
argsBuilder.add(
SourcePathArg.of(
new DefaultBuildTargetSourcePath(getRootTarget(params.getBuildTarget(), target))));
continue;
}
// If we're linking this dep from the body, then we need to link via the giant merged
// libomnibus instead.
if (spec.getBody().containsKey(target)) { // && linkStyle == Linker.LinkableDepType.SHARED) {
if (!alreadyAddedOmnibusToArgs) {
argsBuilder.add(SourcePathArg.of(omnibus));
alreadyAddedOmnibusToArgs = true;
}
continue;
}
// Otherwise, this is either an explicitly statically linked or excluded node, so link it
// normally.
Preconditions.checkState(spec.getExcluded().containsKey(target));
argsBuilder.addAll(nativeLinkable.getNativeLinkableInput(cxxPlatform, linkStyle).getArgs());
}
// Create the root library rule using the arguments assembled above.
BuildTarget rootTarget = getRootTarget(params.getBuildTarget(), rootTargetBase);
NativeLinkTargetMode rootTargetMode = root.getNativeLinkTargetMode(cxxPlatform);
CxxLink rootLinkRule;
switch (rootTargetMode.getType()) {
// Link the root as a shared library.
case SHARED:
{
Optional<String> rootSoname = rootTargetMode.getLibraryName();
rootLinkRule =
CxxLinkableEnhancer.createCxxLinkableSharedBuildRule(
cxxBuckConfig,
cxxPlatform,
params,
ruleResolver,
ruleFinder,
rootTarget,
output.orElse(
BuildTargets.getGenPath(params.getProjectFilesystem(), rootTarget, "%s")
.resolve(
rootSoname.orElse(
String.format(
"%s.%s",
rootTarget.getShortName(),
cxxPlatform.getSharedLibraryExtension())))),
rootSoname,
argsBuilder.build());
break;
}
// Link the root as an executable.
case EXECUTABLE:
{
rootLinkRule =
CxxLinkableEnhancer.createCxxLinkableBuildRule(
cxxBuckConfig,
cxxPlatform,
params,
ruleResolver,
ruleFinder,
rootTarget,
output.orElse(
BuildTargets.getGenPath(params.getProjectFilesystem(), rootTarget, "%s")
.resolve(rootTarget.getShortName())),
argsBuilder.build(),
Linker.LinkableDepType.SHARED,
/* thinLto */ false,
Optional.empty());
break;
}
// $CASES-OMITTED$
default:
throw new IllegalStateException(
String.format(
"%s: unexpected omnibus root type: %s %s",
params.getBuildTarget(), root.getBuildTarget(), rootTargetMode.getType()));
}
CxxLink rootRule = ruleResolver.addToIndex(rootLinkRule);
return OmnibusRoot.of(rootRule.getSourcePathToOutput());
}
protected static OmnibusRoot createRoot(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathRuleFinder ruleFinder,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
ImmutableList<? extends Arg> extraLdflags,
OmnibusSpec spec,
SourcePath omnibus,
NativeLinkTarget root)
throws NoSuchBuildTargetException {
return createRoot(
params,
ruleResolver,
ruleFinder,
cxxBuckConfig,
cxxPlatform,
extraLdflags,
spec,
omnibus,
root,
root.getBuildTarget(),
root.getNativeLinkTargetOutputPath(cxxPlatform));
}
protected static OmnibusRoot createDummyRoot(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathRuleFinder ruleFinder,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
ImmutableList<? extends Arg> extraLdflags,
OmnibusSpec spec,
SourcePath omnibus,
NativeLinkTarget root)
throws NoSuchBuildTargetException {
return createRoot(
params,
ruleResolver,
ruleFinder,
cxxBuckConfig,
cxxPlatform,
extraLdflags,
spec,
omnibus,
root,
getDummyRootTarget(root.getBuildTarget()),
Optional.empty());
}
private static ImmutableList<Arg> createUndefinedSymbolsArgs(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathRuleFinder ruleFinder,
CxxPlatform cxxPlatform,
Iterable<? extends SourcePath> linkerInputs) {
SourcePath undefinedSymbolsFile =
cxxPlatform
.getSymbolNameTool()
.createUndefinedSymbolsFile(
params,
ruleResolver,
ruleFinder,
params
.getBuildTarget()
.withAppendedFlavors(InternalFlavor.of("omnibus-undefined-symbols-file")),
linkerInputs);
return cxxPlatform
.getLd()
.resolve(ruleResolver)
.createUndefinedSymbolsLinkerArgs(
params,
ruleResolver,
ruleFinder,
params
.getBuildTarget()
.withAppendedFlavors(InternalFlavor.of("omnibus-undefined-symbols-args")),
ImmutableList.of(undefinedSymbolsFile));
}
// Create a build rule to link the giant merged omnibus library described by the given spec.
protected static OmnibusLibrary createOmnibus(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathRuleFinder ruleFinder,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
ImmutableList<? extends Arg> extraLdflags,
OmnibusSpec spec)
throws NoSuchBuildTargetException {
ImmutableList.Builder<Arg> argsBuilder = ImmutableList.builder();
// Add extra ldflags to the beginning of the link.
argsBuilder.addAll(extraLdflags);
// For roots that aren't dependencies of nodes in the body, we extract their undefined symbols
// to add to the link so that required symbols get pulled into the merged library.
List<SourcePath> undefinedSymbolsOnlyRoots = new ArrayList<>();
for (BuildTarget target :
Sets.difference(spec.getRoots().keySet(), spec.getGraph().getNodes())) {
NativeLinkTarget linkTarget = Preconditions.checkNotNull(spec.getRoots().get(target));
undefinedSymbolsOnlyRoots.add(
ruleResolver
.requireRule(
getRootTarget(
params.getBuildTarget(),
shouldCreateDummyRoot(linkTarget, cxxPlatform)
? getDummyRootTarget(target)
: target))
.getSourcePathToOutput());
}
argsBuilder.addAll(
createUndefinedSymbolsArgs(
params, ruleResolver, ruleFinder, cxxPlatform, undefinedSymbolsOnlyRoots));
// Walk the graph in topological order, appending each nodes contributions to the link.
ImmutableList<BuildTarget> targets = TopologicalSort.sort(spec.getGraph()).reverse();
for (BuildTarget target : targets) {
// If this is a root, just place the shared library we've linked above onto the link line.
// We need this so that the linker can grab any undefined symbols from it, and therefore
// know which symbols to pull in from the body nodes.
NativeLinkTarget root = spec.getRoots().get(target);
if (root != null) {
argsBuilder.add(
SourcePathArg.of(
((CxxLink)
ruleResolver.requireRule(
getRootTarget(params.getBuildTarget(), root.getBuildTarget())))
.getSourcePathToOutput()));
continue;
}
// Otherwise, this is a body node, and we need to add its static library to the link line,
// so that the linker can discard unused object files from it.
NativeLinkable nativeLinkable = Preconditions.checkNotNull(spec.getBody().get(target));
NativeLinkableInput input =
NativeLinkables.getNativeLinkableInput(
cxxPlatform, Linker.LinkableDepType.STATIC_PIC, nativeLinkable);
argsBuilder.addAll(input.getArgs());
}
// We process all excluded omnibus deps last, and just add their components as if this were a
// normal shared link.
ImmutableMap<BuildTarget, NativeLinkable> deps =
NativeLinkables.getNativeLinkables(
cxxPlatform, spec.getDeps().values(), Linker.LinkableDepType.SHARED);
for (NativeLinkable nativeLinkable : deps.values()) {
NativeLinkableInput input =
NativeLinkables.getNativeLinkableInput(
cxxPlatform, Linker.LinkableDepType.SHARED, nativeLinkable);
argsBuilder.addAll(input.getArgs());
}
// Create the merged omnibus library using the arguments assembled above.
BuildTarget omnibusTarget = params.getBuildTarget().withAppendedFlavors(OMNIBUS_FLAVOR);
String omnibusSoname = getOmnibusSoname(cxxPlatform);
CxxLink omnibusRule =
ruleResolver.addToIndex(
CxxLinkableEnhancer.createCxxLinkableSharedBuildRule(
cxxBuckConfig,
cxxPlatform,
params,
ruleResolver,
ruleFinder,
omnibusTarget,
BuildTargets.getGenPath(params.getProjectFilesystem(), omnibusTarget, "%s")
.resolve(omnibusSoname),
Optional.of(omnibusSoname),
argsBuilder.build()));
return OmnibusLibrary.of(omnibusSoname, omnibusRule.getSourcePathToOutput());
}
/**
* An alternate link strategy for languages which need to package native deps up as shared
* libraries, which only links native nodes which have an explicit edge from non-native code as
* separate, and statically linking all other native nodes into a single giant shared library.
* This reduces the number of shared libraries considerably and also allows the linker to throw
* away a lot of unused object files.
*
* @param nativeLinkTargetRoots root nodes which will be included in the omnibus link.
* @param nativeLinkableRoots root nodes which are to be excluded from the omnibus link.
* @return a map of shared library names to their containing {@link SourcePath}s.
* @throws NoSuchBuildTargetException
*/
public static OmnibusLibraries getSharedLibraries(
BuildRuleParams params,
BuildRuleResolver ruleResolver,
SourcePathRuleFinder ruleFinder,
CxxBuckConfig cxxBuckConfig,
CxxPlatform cxxPlatform,
ImmutableList<? extends Arg> extraLdflags,
Iterable<? extends NativeLinkTarget> nativeLinkTargetRoots,
Iterable<? extends NativeLinkable> nativeLinkableRoots)
throws NoSuchBuildTargetException {
OmnibusLibraries.Builder libs = OmnibusLibraries.builder();
OmnibusSpec spec = buildSpec(cxxPlatform, nativeLinkTargetRoots, nativeLinkableRoots);
// Create an empty dummy omnibus library, to give the roots something to link against before
// we have the actual omnibus library available. Note that this requires that the linker
// supports linking shared libraries with undefined references.
SourcePath dummyOmnibus =
createDummyOmnibus(
params, ruleResolver, ruleFinder, cxxBuckConfig, cxxPlatform, extraLdflags);
// Create rule for each of the root nodes, linking against the dummy omnibus library above.
for (NativeLinkTarget target : spec.getRoots().values()) {
// For executable roots, some platforms can't properly build them when there are any
// unresolved symbols, so we initially link a dummy root just to provide a way to grab the
// undefined symbol list we need to build the real omnibus library.
if (shouldCreateDummyRoot(target, cxxPlatform)) {
createDummyRoot(
params,
ruleResolver,
ruleFinder,
cxxBuckConfig,
cxxPlatform,
extraLdflags,
spec,
dummyOmnibus,
target);
} else {
OmnibusRoot root =
createRoot(
params,
ruleResolver,
ruleFinder,
cxxBuckConfig,
cxxPlatform,
extraLdflags,
spec,
dummyOmnibus,
target);
libs.putRoots(target.getBuildTarget(), root);
}
}
// If there are any body nodes, generate the giant merged omnibus library.
Optional<SourcePath> realOmnibus = Optional.empty();
if (!spec.getBody().isEmpty()) {
OmnibusLibrary omnibus =
createOmnibus(
params, ruleResolver, ruleFinder, cxxBuckConfig, cxxPlatform, extraLdflags, spec);
libs.addLibraries(omnibus);
realOmnibus = Optional.of(omnibus.getPath());
}
// Do another pass over executable roots, building the real DSO which links to the real omnibus.
// See the comment above in the first pass for more details.
for (NativeLinkTarget target : spec.getRoots().values()) {
if (shouldCreateDummyRoot(target, cxxPlatform)) {
OmnibusRoot root =
createRoot(
params,
ruleResolver,
ruleFinder,
cxxBuckConfig,
cxxPlatform,
extraLdflags,
spec,
realOmnibus.orElse(dummyOmnibus),
target);
libs.putRoots(target.getBuildTarget(), root);
}
}
// Lastly, add in any shared libraries from excluded nodes the normal way.
for (NativeLinkable nativeLinkable : spec.getExcluded().values()) {
if (nativeLinkable.getPreferredLinkage(cxxPlatform) != NativeLinkable.Linkage.STATIC) {
for (Map.Entry<String, SourcePath> ent :
nativeLinkable.getSharedLibraries(cxxPlatform).entrySet()) {
libs.addLibraries(OmnibusLibrary.of(ent.getKey(), ent.getValue()));
}
}
}
return libs.build();
}
@Value.Immutable
abstract static class OmnibusSpec {
// The graph containing all root and body nodes that are to be included in the omnibus link.
public abstract DirectedAcyclicGraph<BuildTarget> getGraph();
// All native roots included in the omnibus. These will get linked into separate shared
// libraries which depend on the giant statically linked omnibus body.
public abstract ImmutableMap<BuildTarget, NativeLinkTarget> getRoots();
// All native nodes which are to be statically linked into the giant combined shared library.
public abstract ImmutableMap<BuildTarget, NativeLinkable> getBody();
// All native nodes which are not included in the omnibus link, as either a root or a body node.
public abstract ImmutableMap<BuildTarget, NativeLinkable> getExcluded();
// The subset of excluded nodes which are first-order deps of any root or body nodes.
public abstract ImmutableMap<BuildTarget, NativeLinkable> getDeps();
@Value.Check
public void verify() {
// Verify that all the graph is composed entirely off root and body nodes.
Preconditions.checkState(
ImmutableSet.<BuildTarget>builder()
.addAll(getRoots().keySet())
.addAll(getBody().keySet())
.build()
.containsAll(getGraph().getNodes()));
// Verify that the root, body, and excluded nodes are distinct and that deps are a subset
// of the excluded nodes.
Preconditions.checkState(
Sets.intersection(getRoots().keySet(), getBody().keySet()).isEmpty());
Preconditions.checkState(
Sets.intersection(getRoots().keySet(), getExcluded().keySet()).isEmpty());
Preconditions.checkState(
Sets.intersection(getBody().keySet(), getExcluded().keySet()).isEmpty());
Preconditions.checkState(getExcluded().keySet().containsAll(getDeps().keySet()));
}
}
@Value.Immutable
@BuckStyleImmutable
interface AbstractOmnibusRoot {
@Value.Parameter
SourcePath getPath();
}
@Value.Immutable
@BuckStyleImmutable
interface AbstractOmnibusLibrary {
@Value.Parameter
String getSoname();
@Value.Parameter
SourcePath getPath();
}
@Value.Immutable
@BuckStyleImmutable
abstract static class AbstractOmnibusLibraries {
@Value.Parameter
public abstract ImmutableMap<BuildTarget, OmnibusRoot> getRoots();
@Value.Parameter
public abstract ImmutableList<OmnibusLibrary> getLibraries();
}
}