com.facebook.buck.cxx.NativeLinkables.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.cxx.NativeLinkables.java

Source

/*
 * Copyright 2014-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.MutableDirectedGraph;
import com.facebook.buck.graph.TopologicalSort;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

public class NativeLinkables {

    private NativeLinkables() {
    }

    /**
     * Find all {@link NativeLinkable} transitive roots reachable from the given {@link BuildRule}s.
     *
     * @param from the starting set of {@link BuildRule}s to begin the search from.
     * @param traverse a {@link Predicate} determining acceptable dependencies to traverse when
     *                 searching for {@link NativeLinkable}s.
     * @param skip Skip this {@link BuildRule} even if it is an instance of {@link NativeLinkable}
     * @return all the roots found as a map from {@link BuildTarget} to {@link NativeLinkable}.
     */
    static ImmutableMap<BuildTarget, NativeLinkable> getNativeLinkableRoots(Iterable<? extends BuildRule> from,
            final Predicate<Object> traverse, final Predicate<Object> skip) {

        final ImmutableMap.Builder<BuildTarget, NativeLinkable> nativeLinkables = ImmutableMap.builder();
        AbstractBreadthFirstTraversal<BuildRule> visitor = new AbstractBreadthFirstTraversal<BuildRule>(from) {
            @Override
            public ImmutableSet<BuildRule> visit(BuildRule rule) {

                // If this is `NativeLinkable`, we've found a root so record the rule and terminate
                // the search.
                if (rule instanceof NativeLinkable && !skip.apply(rule)) {
                    NativeLinkable nativeLinkable = (NativeLinkable) rule;
                    nativeLinkables.put(nativeLinkable.getBuildTarget(), nativeLinkable);
                    return ImmutableSet.of();
                }

                // Otherwise, make sure this rule is marked as traversable before following it's deps.
                if (traverse.apply(rule)) {
                    return rule.getDeps();
                }

                return ImmutableSet.of();
            }
        };
        visitor.start();

        return nativeLinkables.build();
    }

    /**
     * Find all {@link NativeLinkable} transitive roots reachable from the given {@link BuildRule}s.
     *
     * @param from the starting set of {@link BuildRule}s to begin the search from.
     * @param traverse a {@link Predicate} determining acceptable dependencies to traverse when
     *                 searching for {@link NativeLinkable}s.
     * @return all the roots found as a map from {@link BuildTarget} to {@link NativeLinkable}.
     */
    public static ImmutableMap<BuildTarget, NativeLinkable> getNativeLinkableRoots(
            Iterable<? extends BuildRule> from, final Predicate<Object> traverse) {
        return getNativeLinkableRoots(from, traverse, x -> false);
    }

    /**
     * Extract from the dependency graph all the libraries which must be considered for linking.
     *
     * Traversal proceeds depending on whether each dependency is to be statically or dynamically
     * linked.
     *
     * @param linkStyle how dependencies should be linked, if their preferred_linkage is
     *                  {@code NativeLinkable.Linkage.ANY}.
     */
    public static ImmutableMap<BuildTarget, NativeLinkable> getNativeLinkables(final CxxPlatform cxxPlatform,
            Iterable<? extends NativeLinkable> inputs, final Linker.LinkableDepType linkStyle,
            final Predicate<? super NativeLinkable> traverse) {

        final Map<BuildTarget, NativeLinkable> nativeLinkables = Maps.newHashMap();
        for (NativeLinkable nativeLinkable : inputs) {
            nativeLinkables.put(nativeLinkable.getBuildTarget(), nativeLinkable);
        }

        final MutableDirectedGraph<BuildTarget> graph = new MutableDirectedGraph<>();
        AbstractBreadthFirstTraversal<BuildTarget> visitor = new AbstractBreadthFirstTraversal<BuildTarget>(
                nativeLinkables.keySet()) {
            @Override
            public ImmutableSet<BuildTarget> visit(BuildTarget target) {
                NativeLinkable nativeLinkable = Preconditions.checkNotNull(nativeLinkables.get(target));
                graph.addNode(target);

                // We always traverse a rule's exported native linkables.
                Iterable<? extends NativeLinkable> nativeLinkableDeps = nativeLinkable
                        .getNativeLinkableExportedDepsForPlatform(cxxPlatform);

                boolean shouldTraverse = true;
                switch (nativeLinkable.getPreferredLinkage(cxxPlatform)) {
                case ANY:
                    shouldTraverse = linkStyle != Linker.LinkableDepType.SHARED;
                    break;
                case SHARED:
                    shouldTraverse = false;
                    break;
                case STATIC:
                    shouldTraverse = true;
                    break;
                }

                // If we're linking this dependency statically, we also need to traverse its deps.
                if (shouldTraverse) {
                    nativeLinkableDeps = Iterables.concat(nativeLinkableDeps,
                            nativeLinkable.getNativeLinkableDepsForPlatform(cxxPlatform));
                }

                // Process all the traversable deps.
                ImmutableSet.Builder<BuildTarget> deps = ImmutableSet.builder();
                for (NativeLinkable dep : nativeLinkableDeps) {
                    if (traverse.apply(dep)) {
                        BuildTarget depTarget = dep.getBuildTarget();
                        graph.addEdge(target, depTarget);
                        deps.add(depTarget);
                        nativeLinkables.put(depTarget, dep);
                    }
                }
                return deps.build();
            }
        };
        visitor.start();

        // Topologically sort the rules.
        Iterable<BuildTarget> ordered = TopologicalSort.sort(graph).reverse();

        // Return a map of of the results.
        ImmutableMap.Builder<BuildTarget, NativeLinkable> result = ImmutableMap.builder();
        for (BuildTarget target : ordered) {
            result.put(target, nativeLinkables.get(target));
        }
        return result.build();
    }

    public static ImmutableMap<BuildTarget, NativeLinkable> getNativeLinkables(final CxxPlatform cxxPlatform,
            Iterable<? extends NativeLinkable> inputs, final Linker.LinkableDepType linkStyle) {
        return getNativeLinkables(cxxPlatform, inputs, linkStyle, x -> true);
    }

    public static Linker.LinkableDepType getLinkStyle(NativeLinkable.Linkage preferredLinkage,
            Linker.LinkableDepType requestedLinkStyle) {
        Linker.LinkableDepType linkStyle;
        switch (preferredLinkage) {
        case SHARED:
            linkStyle = Linker.LinkableDepType.SHARED;
            break;
        case STATIC:
            linkStyle = requestedLinkStyle == Linker.LinkableDepType.STATIC ? Linker.LinkableDepType.STATIC
                    : Linker.LinkableDepType.STATIC_PIC;
            break;
        case ANY:
            linkStyle = requestedLinkStyle;
            break;
        default:
            throw new IllegalStateException();
        }
        return linkStyle;
    }

    public static NativeLinkableInput getNativeLinkableInput(CxxPlatform cxxPlatform,
            Linker.LinkableDepType linkStyle, NativeLinkable nativeLinkable) throws NoSuchBuildTargetException {
        NativeLinkable.Linkage link = nativeLinkable.getPreferredLinkage(cxxPlatform);
        return nativeLinkable.getNativeLinkableInput(cxxPlatform, getLinkStyle(link, linkStyle));
    }

    /**
     * Collect up and merge all {@link com.facebook.buck.cxx.NativeLinkableInput} objects from
     * transitively traversing all unbroken dependency chains of
     * {@link com.facebook.buck.cxx.NativeLinkable} objects found via the passed in
     * {@link com.facebook.buck.rules.BuildRule} roots.
     */
    public static NativeLinkableInput getTransitiveNativeLinkableInput(CxxPlatform cxxPlatform,
            Iterable<? extends BuildRule> inputs, Linker.LinkableDepType depType, Predicate<Object> traverse,
            Predicate<Object> skip) throws NoSuchBuildTargetException {

        // Get the topologically sorted native linkables.
        ImmutableMap<BuildTarget, NativeLinkable> roots = getNativeLinkableRoots(inputs, traverse, skip);
        ImmutableMap<BuildTarget, NativeLinkable> nativeLinkables = getNativeLinkables(cxxPlatform, roots.values(),
                depType);
        ImmutableList.Builder<NativeLinkableInput> nativeLinkableInputs = ImmutableList.builder();
        for (NativeLinkable nativeLinkable : nativeLinkables.values()) {
            nativeLinkableInputs.add(getNativeLinkableInput(cxxPlatform, depType, nativeLinkable));
        }
        return NativeLinkableInput.concat(nativeLinkableInputs.build());
    }

    public static ImmutableMap<BuildTarget, NativeLinkable> getTransitiveNativeLinkables(
            final CxxPlatform cxxPlatform, Iterable<? extends NativeLinkable> inputs) {

        final Map<BuildTarget, NativeLinkable> nativeLinkables = Maps.newHashMap();
        for (NativeLinkable nativeLinkable : inputs) {
            nativeLinkables.put(nativeLinkable.getBuildTarget(), nativeLinkable);
        }

        final MutableDirectedGraph<BuildTarget> graph = new MutableDirectedGraph<>();
        AbstractBreadthFirstTraversal<BuildTarget> visitor = new AbstractBreadthFirstTraversal<BuildTarget>(
                nativeLinkables.keySet()) {
            @Override
            public ImmutableSet<BuildTarget> visit(BuildTarget target) {
                NativeLinkable nativeLinkable = Preconditions.checkNotNull(nativeLinkables.get(target));
                graph.addNode(target);
                ImmutableSet.Builder<BuildTarget> deps = ImmutableSet.builder();
                for (NativeLinkable dep : Iterables.concat(
                        nativeLinkable.getNativeLinkableDepsForPlatform(cxxPlatform),
                        nativeLinkable.getNativeLinkableExportedDepsForPlatform(cxxPlatform))) {
                    BuildTarget depTarget = dep.getBuildTarget();
                    graph.addEdge(target, depTarget);
                    deps.add(depTarget);
                    nativeLinkables.put(depTarget, dep);
                }
                return deps.build();
            }
        };
        visitor.start();

        return ImmutableMap.copyOf(nativeLinkables);
    }

    /**
     * Collect up and merge all {@link com.facebook.buck.cxx.NativeLinkableInput} objects from
     * transitively traversing all unbroken dependency chains of
     * {@link com.facebook.buck.cxx.NativeLinkable} objects found via the passed in
     * {@link com.facebook.buck.rules.BuildRule} roots.
     */
    public static NativeLinkableInput getTransitiveNativeLinkableInput(CxxPlatform cxxPlatform,
            Iterable<? extends BuildRule> inputs, Linker.LinkableDepType depType, Predicate<Object> traverse)
            throws NoSuchBuildTargetException {
        return getTransitiveNativeLinkableInput(cxxPlatform, inputs, depType, traverse, x -> false);
    }

    /**
     * Collect all the shared libraries generated by {@link NativeLinkable}s found by transitively
     * traversing all unbroken dependency chains of {@link com.facebook.buck.cxx.NativeLinkable}
     * objects found via the passed in {@link com.facebook.buck.rules.BuildRule} roots.
     *
     * @return a mapping of library name to the library {@link SourcePath}.
     */
    public static ImmutableSortedMap<String, SourcePath> getTransitiveSharedLibraries(CxxPlatform cxxPlatform,
            Iterable<? extends BuildRule> inputs, Predicate<Object> traverse, Predicate<Object> skip)
            throws NoSuchBuildTargetException {

        ImmutableMap<BuildTarget, NativeLinkable> roots = getNativeLinkableRoots(inputs, traverse, skip);
        ImmutableMap<BuildTarget, NativeLinkable> nativeLinkables = getTransitiveNativeLinkables(cxxPlatform,
                roots.values());

        Map<String, SourcePath> libraries = new LinkedHashMap<>();
        for (NativeLinkable nativeLinkable : nativeLinkables.values()) {
            NativeLinkable.Linkage linkage = nativeLinkable.getPreferredLinkage(cxxPlatform);
            if (linkage != NativeLinkable.Linkage.STATIC) {
                ImmutableMap<String, SourcePath> libs = nativeLinkable.getSharedLibraries(cxxPlatform);
                for (Map.Entry<String, SourcePath> lib : libs.entrySet()) {
                    SourcePath prev = libraries.put(lib.getKey(), lib.getValue());
                    if (prev != null && !prev.equals(lib.getValue())) {
                        throw new HumanReadableException("conflicting libraries for key %s: %s != %s", lib.getKey(),
                                lib.getValue(), prev);
                    }
                }
            }
        }
        return ImmutableSortedMap.copyOf(libraries);
    }

    /**
     * Collect all the shared libraries generated by {@link NativeLinkable}s found by transitively
     * traversing all unbroken dependency chains of {@link com.facebook.buck.cxx.NativeLinkable}
     * objects found via the passed in {@link com.facebook.buck.rules.BuildRule} roots.
     *
     * @return a mapping of library name to the library {@link SourcePath}.
     */
    public static ImmutableSortedMap<String, SourcePath> getTransitiveSharedLibraries(CxxPlatform cxxPlatform,
            Iterable<? extends BuildRule> inputs, Predicate<Object> traverse) throws NoSuchBuildTargetException {
        return getTransitiveSharedLibraries(cxxPlatform, inputs, traverse, x -> false);
    }

    /**
     * @return the {@link NativeLinkTarget} that can be extracted from {@code object}, if any.
     */
    public static Optional<NativeLinkTarget> getNativeLinkTarget(Object object, CxxPlatform cxxPlatform) {
        if (object instanceof NativeLinkTarget) {
            return Optional.of((NativeLinkTarget) object);
        }
        if (object instanceof CanProvideNativeLinkTarget) {
            return ((CanProvideNativeLinkTarget) object).getNativeLinkTarget(cxxPlatform);
        }
        return Optional.empty();
    }

}