/* * Copyright 2016-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.model.BuildTarget; import com.facebook.buck.util.immutables.BuckStyleTuple; import com.google.common.base.Predicates; 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 java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import org.immutables.value.Value; /** * A helper class for building the included and excluded omnibus roots to pass to the omnibus * builder. */ @Value.Immutable @BuckStyleTuple abstract class AbstractOmnibusRoots { /** @return the {@link NativeLinkTarget} roots that are included in omnibus linking. */ abstract ImmutableMap<BuildTarget, NativeLinkTarget> getIncludedRoots(); /** @return the {@link NativeLinkable} roots that are excluded from omnibus linking. */ abstract ImmutableMap<BuildTarget, NativeLinkable> getExcludedRoots(); public static Builder builder(CxxPlatform cxxPlatform, ImmutableSet<BuildTarget> excludes) { return new Builder(cxxPlatform, excludes); } public static class Builder { private final CxxPlatform cxxPlatform; private final ImmutableSet<BuildTarget> excludes; private final Map<BuildTarget, NativeLinkTarget> includedRoots = new LinkedHashMap<>(); private final Map<BuildTarget, NativeLinkable> excludedRoots = new LinkedHashMap<>(); private Builder(CxxPlatform cxxPlatform, ImmutableSet<BuildTarget> excludes) { this.cxxPlatform = cxxPlatform; this.excludes = excludes; } /** Add a root which is included in omnibus linking. */ public void addIncludedRoot(NativeLinkTarget root) { includedRoots.put(root.getBuildTarget(), root); } /** Add a root which is excluded from omnibus linking. */ public void addExcludedRoot(NativeLinkable root) { excludedRoots.put(root.getBuildTarget(), root); } /** * Add a node which may qualify as either an included root, and excluded root, or neither. * * @return whether the node was added as a root. */ public void addPotentialRoot(NativeLinkable node) { Optional<NativeLinkTarget> target = NativeLinkables.getNativeLinkTarget(node, cxxPlatform); if (target.isPresent() && !excludes.contains(node.getBuildTarget())) { addIncludedRoot(target.get()); } else { addExcludedRoot(node); } } private ImmutableMap<BuildTarget, NativeLinkable> buildExcluded() { final Map<BuildTarget, NativeLinkable> excluded = new LinkedHashMap<>(); excluded.putAll(excludedRoots); // Find all excluded nodes reachable from the included roots. Map<BuildTarget, NativeLinkable> includedRootDeps = new LinkedHashMap<>(); for (NativeLinkTarget target : includedRoots.values()) { for (NativeLinkable linkable : target.getNativeLinkTargetDeps(cxxPlatform)) { includedRootDeps.put(linkable.getBuildTarget(), linkable); } } new AbstractBreadthFirstTraversal<NativeLinkable>(includedRootDeps.values()) { @Override public Iterable<NativeLinkable> visit(NativeLinkable linkable) throws RuntimeException { if (linkable.getPreferredLinkage(cxxPlatform) == NativeLinkable.Linkage.SHARED) { excluded.put(linkable.getBuildTarget(), linkable); return ImmutableSet.of(); } return Iterables.concat( linkable.getNativeLinkableDepsForPlatform(cxxPlatform), linkable.getNativeLinkableExportedDepsForPlatform(cxxPlatform)); } }.start(); // Prepare the final map of excluded roots, starting with the pre-defined ones. final Map<BuildTarget, NativeLinkable> updatedExcludedRoots = new LinkedHashMap<>(); updatedExcludedRoots.putAll(excludedRoots); // Recursively expand the excluded nodes including any preloaded deps, as we'll need this full // list to know which roots to exclude from omnibus linking. new AbstractBreadthFirstTraversal<NativeLinkable>(excluded.values()) { @Override public Iterable<NativeLinkable> visit(NativeLinkable linkable) { if (includedRoots.containsKey(linkable.getBuildTarget())) { updatedExcludedRoots.put(linkable.getBuildTarget(), linkable); } return Iterables.concat( linkable.getNativeLinkableDepsForPlatform(cxxPlatform), linkable.getNativeLinkableExportedDepsForPlatform(cxxPlatform)); } }.start(); return ImmutableMap.copyOf(updatedExcludedRoots); } private ImmutableMap<BuildTarget, NativeLinkTarget> buildIncluded( ImmutableSet<BuildTarget> excluded) { return ImmutableMap.copyOf( Maps.filterKeys(includedRoots, Predicates.not(excluded::contains))); } public boolean isEmpty() { return includedRoots.isEmpty() && excludedRoots.isEmpty(); } public OmnibusRoots build() { ImmutableMap<BuildTarget, NativeLinkable> excluded = buildExcluded(); ImmutableMap<BuildTarget, NativeLinkTarget> included = buildIncluded(excluded.keySet()); return OmnibusRoots.of(included, excluded); } } }