com.facebook.buck.apple.AppleBuildRules.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.apple.AppleBuildRules.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.apple;

import com.facebook.buck.cxx.CxxLibraryDescription;
import com.facebook.buck.graph.AcyclicDepthFirstPostOrderTraversal;
import com.facebook.buck.graph.GraphTraversable;
import com.facebook.buck.halide.HalideLibraryDescription;
import com.facebook.buck.log.Logger;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.TargetNode;
import com.facebook.buck.swift.SwiftLibraryDescription;
import com.facebook.buck.util.MoreCollectors;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;

import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

/**
 * Helpers for reading properties of Apple target build rules.
 */
public final class AppleBuildRules {

    private static final Logger LOG = Logger.get(AppleBuildRules.class);

    // Utility class not to be instantiated.
    private AppleBuildRules() {
    }

    @SuppressWarnings("unchecked")
    public static final ImmutableSet<Class<? extends Description<?>>> XCODE_TARGET_DESCRIPTION_CLASSES = ImmutableSet
            .of(AppleLibraryDescription.class, CxxLibraryDescription.class, AppleBinaryDescription.class,
                    AppleBundleDescription.class, AppleTestDescription.class, HalideLibraryDescription.class);

    private static final ImmutableSet<Class<? extends BuildRule>> XCODE_TARGET_BUILD_RULE_TEST_TYPES = ImmutableSet
            .of(AppleTest.class);

    private static final ImmutableSet<Class<? extends Description<?>>> RECURSIVE_DEPENDENCIES_STOP_AT_DESCRIPTION_CLASSES = ImmutableSet
            .of(AppleBundleDescription.class, AppleResourceDescription.class);

    private static final ImmutableSet<AppleBundleExtension> XCODE_TARGET_TEST_BUNDLE_EXTENSIONS = ImmutableSet
            .of(AppleBundleExtension.XCTEST);

    private static final ImmutableSet<Class<? extends Description<?>>> WRAPPER_RESOURCE_DESCRIPTION_CLASSES = ImmutableSet
            .of(CoreDataModelDescription.class, SceneKitAssetsDescription.class);

    private static final ImmutableSet<Class<? extends Description<?>>> APPLE_ASSET_CATALOG_DESCRIPTION_CLASSES = ImmutableSet
            .of(AppleAssetCatalogDescription.class);

    public static final ImmutableSet<Class<? extends Description<?>>> CORE_DATA_MODEL_DESCRIPTION_CLASSES = ImmutableSet
            .of(CoreDataModelDescription.class);

    public static final ImmutableSet<Class<? extends Description<?>>> SCENEKIT_ASSETS_DESCRIPTION_CLASSES = ImmutableSet
            .of(SceneKitAssetsDescription.class);

    /**
     * Whether the build rule type is equivalent to some kind of Xcode target.
     */
    public static boolean isXcodeTargetDescription(Description<?> description) {
        return XCODE_TARGET_DESCRIPTION_CLASSES.contains(description.getClass());
    }

    /**
     * Whether the build rule type is a test target.
     */
    public static boolean isXcodeTargetTestBuildRule(BuildRule rule) {
        return XCODE_TARGET_BUILD_RULE_TEST_TYPES.contains(rule.getClass());
    }

    /**
     * Whether the bundle extension is a test bundle extension.
     */
    public static boolean isXcodeTargetTestBundleExtension(AppleBundleExtension extension) {
        return XCODE_TARGET_TEST_BUNDLE_EXTENSIONS.contains(extension);
    }

    public static String getOutputFileNameFormatForLibrary(boolean isSharedLibrary) {
        if (isSharedLibrary) {
            return "lib%s.dylib";
        } else {
            return "lib%s.a";
        }
    }

    public enum RecursiveDependenciesMode {
        /**
         * Will traverse all rules that are built.
         */
        BUILDING,
        /**
         * Will also not traverse the dependencies of bundles, as those are copied inside the bundle.
         */
        COPYING,
        /**
         * Will also not traverse the dependencies of shared libraries, as those are linked already.
         */
        LINKING,
    }

    public static ImmutableSet<TargetNode<?, ?>> getRecursiveTargetNodeDependenciesOfTypes(
            final TargetGraph targetGraph, final Optional<AppleDependenciesCache> cache,
            final RecursiveDependenciesMode mode, final TargetNode<?, ?> targetNode,
            final Optional<ImmutableSet<Class<? extends Description<?>>>> descriptionClasses) {
        LOG.verbose("Getting recursive dependencies of node %s, mode %s, including only types %s\n", targetNode,
                mode, descriptionClasses);

        GraphTraversable<TargetNode<?, ?>> graphTraversable = node -> {
            if (!isXcodeTargetDescription(node.getDescription())
                    || SwiftLibraryDescription.isSwiftTarget(node.getBuildTarget())) {
                return Collections.emptyIterator();
            }

            LOG.verbose("Finding children of node: %s", node);

            ImmutableSortedSet<TargetNode<?, ?>> defaultDeps;
            ImmutableSortedSet<TargetNode<?, ?>> exportedDeps;
            if (!cache.isPresent()) {
                ImmutableSortedSet.Builder<TargetNode<?, ?>> defaultDepsBuilder = ImmutableSortedSet.naturalOrder();
                ImmutableSortedSet.Builder<TargetNode<?, ?>> exportedDepsBuilder = ImmutableSortedSet
                        .naturalOrder();
                addDirectAndExportedDeps(targetGraph, node, defaultDepsBuilder, exportedDepsBuilder);
                defaultDeps = defaultDepsBuilder.build();
                exportedDeps = exportedDepsBuilder.build();
            } else {
                defaultDeps = cache.get().getDefaultDeps(node);
                exportedDeps = cache.get().getExportedDeps(node);
            }

            if (node.getDescription() instanceof AppleBundleDescription) {
                AppleBundleDescription.Arg arg = (AppleBundleDescription.Arg) node.getConstructorArg();

                ImmutableSortedSet.Builder<TargetNode<?, ?>> editedDeps = ImmutableSortedSet.naturalOrder();
                ImmutableSortedSet.Builder<TargetNode<?, ?>> editedExportedDeps = ImmutableSortedSet.naturalOrder();
                for (TargetNode<?, ?> rule : defaultDeps) {
                    if (!rule.getBuildTarget().equals(arg.binary)) {
                        editedDeps.add(rule);
                    } else {
                        addDirectAndExportedDeps(targetGraph, targetGraph.get(arg.binary), editedDeps,
                                editedExportedDeps);
                    }
                }

                ImmutableSortedSet<TargetNode<?, ?>> newDefaultDeps = editedDeps.build();
                ImmutableSortedSet<TargetNode<?, ?>> newExportedDeps = editedExportedDeps.build();
                LOG.verbose("Transformed deps for bundle %s: %s -> %s, exported deps %s -> %s", node, defaultDeps,
                        newDefaultDeps, exportedDeps, newExportedDeps);
                defaultDeps = newDefaultDeps;
                exportedDeps = newExportedDeps;
            }

            LOG.verbose("Default deps for node %s mode %s: %s", node, mode, defaultDeps);
            if (!exportedDeps.isEmpty()) {
                LOG.verbose("Exported deps for node %s mode %s: %s", node, mode, exportedDeps);
            }

            ImmutableSortedSet<TargetNode<?, ?>> deps = ImmutableSortedSet.of();

            if (node != targetNode) {
                switch (mode) {
                case LINKING:
                    boolean nodeIsAppleLibrary = node.getDescription() instanceof AppleLibraryDescription;
                    boolean nodeIsCxxLibrary = node.getDescription() instanceof CxxLibraryDescription;
                    if (nodeIsAppleLibrary || nodeIsCxxLibrary) {
                        if (AppleLibraryDescription.isSharedLibraryTarget(node.getBuildTarget())) {
                            deps = exportedDeps;
                        } else {
                            deps = defaultDeps;
                        }
                    } else if (RECURSIVE_DEPENDENCIES_STOP_AT_DESCRIPTION_CLASSES
                            .contains(node.getDescription().getClass())) {
                        deps = exportedDeps;
                    } else {
                        deps = defaultDeps;
                    }
                    break;
                case COPYING:
                    if (RECURSIVE_DEPENDENCIES_STOP_AT_DESCRIPTION_CLASSES
                            .contains(node.getDescription().getClass())) {
                        deps = exportedDeps;
                    } else {
                        deps = defaultDeps;
                    }
                    break;
                case BUILDING:
                    deps = defaultDeps;
                    break;
                }
            } else {
                deps = defaultDeps;
            }

            LOG.verbose("Walking children of node %s: %s", node, deps);
            return deps.iterator();
        };

        final ImmutableSet.Builder<TargetNode<?, ?>> filteredRules = ImmutableSet.builder();
        AcyclicDepthFirstPostOrderTraversal<TargetNode<?, ?>> traversal = new AcyclicDepthFirstPostOrderTraversal<>(
                graphTraversable);
        try {
            for (TargetNode<?, ?> node : traversal.traverse(ImmutableList.of(targetNode))) {
                if (node != targetNode && (!descriptionClasses.isPresent()
                        || descriptionClasses.get().contains(node.getDescription().getClass()))) {
                    filteredRules.add(node);
                }
            }
        } catch (AcyclicDepthFirstPostOrderTraversal.CycleException e) {
            // actual load failures and cycle exceptions should have been caught at an earlier stage
            throw new RuntimeException(e);
        }
        ImmutableSet<TargetNode<?, ?>> result = filteredRules.build();
        LOG.verbose("Got recursive dependencies of node %s mode %s types %s: %s\n", targetNode, mode,
                descriptionClasses, result);

        return result;
    }

    static void addDirectAndExportedDeps(TargetGraph targetGraph, TargetNode<?, ?> targetNode,
            ImmutableSortedSet.Builder<TargetNode<?, ?>> directDepsBuilder,
            ImmutableSortedSet.Builder<TargetNode<?, ?>> exportedDepsBuilder) {
        directDepsBuilder.addAll(targetGraph.getAll(targetNode.getDepsStream()::iterator));
        if (targetNode.getDescription() instanceof AppleLibraryDescription
                || targetNode.getDescription() instanceof CxxLibraryDescription) {
            CxxLibraryDescription.Arg arg = (CxxLibraryDescription.Arg) targetNode.getConstructorArg();
            LOG.verbose("Exported deps of node %s: %s", targetNode, arg.exportedDeps);
            Iterable<TargetNode<?, ?>> exportedNodes = targetGraph.getAll(arg.exportedDeps);
            directDepsBuilder.addAll(exportedNodes);
            exportedDepsBuilder.addAll(exportedNodes);
        }
    }

    public static ImmutableSet<TargetNode<?, ?>> getSchemeBuildableTargetNodes(TargetGraph targetGraph,
            Optional<AppleDependenciesCache> cache, TargetNode<?, ?> targetNode) {
        Iterable<TargetNode<?, ?>> targetNodes = Iterables.concat(
                getRecursiveTargetNodeDependenciesOfTypes(targetGraph, cache, RecursiveDependenciesMode.BUILDING,
                        targetNode, Optional.empty()),
                ImmutableSet.of(targetNode));

        return ImmutableSet
                .copyOf(Iterables.filter(targetNodes, input -> isXcodeTargetDescription(input.getDescription())));
    }

    public static Function<TargetNode<?, ?>, ImmutableSet<TargetNode<?, ?>>> newRecursiveRuleDependencyTransformer(
            final TargetGraph targetGraph, final Optional<AppleDependenciesCache> cache,
            final RecursiveDependenciesMode mode,
            final ImmutableSet<Class<? extends Description<?>>> descriptionClasses) {
        return input -> getRecursiveTargetNodeDependenciesOfTypes(targetGraph, cache, mode, input,
                Optional.of(descriptionClasses));
    }

    public static <T> ImmutableSet<AppleAssetCatalogDescription.Arg> collectRecursiveAssetCatalogs(
            TargetGraph targetGraph, Optional<AppleDependenciesCache> cache,
            Iterable<TargetNode<T, ?>> targetNodes) {
        return FluentIterable.from(targetNodes)
                .transformAndConcat(newRecursiveRuleDependencyTransformer(targetGraph, cache,
                        RecursiveDependenciesMode.COPYING, APPLE_ASSET_CATALOG_DESCRIPTION_CLASSES))
                .transform(input -> (AppleAssetCatalogDescription.Arg) input.getConstructorArg()).toSet();
    }

    public static <T> ImmutableSet<AppleWrapperResourceArg> collectRecursiveWrapperResources(
            TargetGraph targetGraph, Optional<AppleDependenciesCache> cache,
            Iterable<TargetNode<T, ?>> targetNodes) {
        return FluentIterable.from(targetNodes)
                .transformAndConcat(newRecursiveRuleDependencyTransformer(targetGraph, cache,
                        RecursiveDependenciesMode.COPYING, WRAPPER_RESOURCE_DESCRIPTION_CLASSES))
                .transform(input -> (AppleWrapperResourceArg) input.getConstructorArg()).toSet();
    }

    @SuppressWarnings("unchecked")
    public static <T, U extends Description<T>> ImmutableSet<T> collectTransitiveBuildRules(TargetGraph targetGraph,
            Optional<AppleDependenciesCache> cache,
            ImmutableSet<Class<? extends Description<?>>> descriptionClasses,
            Collection<TargetNode<?, ?>> targetNodes) {
        return targetNodes.stream()
                .flatMap(targetNode -> newRecursiveRuleDependencyTransformer(targetGraph, cache,
                        RecursiveDependenciesMode.COPYING, descriptionClasses).apply(targetNode).stream())
                .map(input -> (T) input.getConstructorArg()).collect(MoreCollectors.toImmutableSet());
    }

    public static ImmutableSet<AppleAssetCatalogDescription.Arg> collectDirectAssetCatalogs(TargetGraph targetGraph,
            TargetNode<?, ?> targetNode) {
        ImmutableSet.Builder<AppleAssetCatalogDescription.Arg> builder = ImmutableSet.builder();
        Iterable<TargetNode<?, ?>> deps = targetGraph.getAll(targetNode.getDeps());
        for (TargetNode<?, ?> node : deps) {
            if (node.getDescription() instanceof AppleAssetCatalogDescription) {
                builder.add((AppleAssetCatalogDescription.Arg) node.getConstructorArg());
            }
        }
        return builder.build();
    }

}