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

Java tutorial

Introduction

Here is the source code for com.facebook.buck.cxx.CxxPreprocessables.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.AbstractBreadthFirstThrowingTraversal;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.Flavor;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.coercer.FrameworkPath;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Suppliers;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;

import org.immutables.value.Value;

import java.nio.file.Path;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.Nonnull;

public class CxxPreprocessables {

    private CxxPreprocessables() {
    }

    public enum HeaderMode {
        /**
         * Creates the tree of symbolic links of headers.
         */
        SYMLINK_TREE_ONLY,
        /**
         * Creates the header map that references the headers directly in the source tree.
         */
        HEADER_MAP_ONLY,
        /**
         * Creates the tree of symbolic links of headers and creates the header map that
         * references the symbolic links to the headers.
         */
        SYMLINK_TREE_WITH_HEADER_MAP,
    }

    public enum IncludeType {

        /**
         * Headers should be included with `-I`.
         */
        LOCAL {
            @Override
            public Iterable<String> includeArgs(Preprocessor pp, Iterable<String> includeRoots) {
                return pp.localIncludeArgs(includeRoots);
            }
        },

        /**
         * Headers should be included with `-isystem`.
         */
        SYSTEM {
            @Override
            public Iterable<String> includeArgs(Preprocessor pp, Iterable<String> includeRoots) {
                return pp.systemIncludeArgs(includeRoots);
            }
        },

        /**
         * Headers should be included with `-iquote`.
         */
        IQUOTE {
            @Override
            public Iterable<String> includeArgs(Preprocessor pp, Iterable<String> includeRoots) {
                return pp.quoteIncludeArgs(includeRoots);
            }
        },;

        public abstract Iterable<String> includeArgs(Preprocessor pp, Iterable<String> includeRoots);

    }

    /**
     * Resolve the map of name to {@link SourcePath} to a map of full header name to
     * {@link SourcePath}.
     */
    public static ImmutableMap<Path, SourcePath> resolveHeaderMap(Path basePath,
            ImmutableMap<String, SourcePath> headers) {

        ImmutableMap.Builder<Path, SourcePath> headerMap = ImmutableMap.builder();

        // Resolve the "names" of the headers to actual paths by prepending the base path
        // specified by the build target.
        for (ImmutableMap.Entry<String, SourcePath> ent : headers.entrySet()) {
            Path path = basePath.resolve(ent.getKey());
            headerMap.put(path, ent.getValue());
        }

        return headerMap.build();
    }

    /**
     * Find and return the {@link CxxPreprocessorInput} objects from {@link CxxPreprocessorDep}
     * found while traversing the dependencies starting from the {@link BuildRule} objects given.
     */
    public static Collection<CxxPreprocessorInput> getTransitiveCxxPreprocessorInput(final CxxPlatform cxxPlatform,
            Iterable<? extends BuildRule> inputs, final Predicate<Object> traverse)
            throws NoSuchBuildTargetException {

        // We don't really care about the order we get back here, since headers shouldn't
        // conflict.  However, we want something that's deterministic, so sort by build
        // target.
        final Map<BuildTarget, CxxPreprocessorInput> deps = Maps.newLinkedHashMap();

        // Build up the map of all C/C++ preprocessable dependencies.
        new AbstractBreadthFirstThrowingTraversal<BuildRule, NoSuchBuildTargetException>(inputs) {
            @Override
            public ImmutableSet<BuildRule> visit(BuildRule rule) throws NoSuchBuildTargetException {
                if (rule instanceof CxxPreprocessorDep) {
                    CxxPreprocessorDep dep = (CxxPreprocessorDep) rule;
                    deps.putAll(dep.getTransitiveCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC));
                    return ImmutableSet.of();
                }
                return traverse.apply(rule) ? rule.getDeps() : ImmutableSet.of();
            }
        }.start();

        // Grab the cxx preprocessor inputs and return them.
        return deps.values();
    }

    public static Collection<CxxPreprocessorInput> getTransitiveCxxPreprocessorInput(final CxxPlatform cxxPlatform,
            Iterable<? extends BuildRule> inputs) throws NoSuchBuildTargetException {
        return getTransitiveCxxPreprocessorInput(cxxPlatform, inputs, x -> true);
    }

    /**
     * Build the {@link HeaderSymlinkTree} rule using the original build params from a target node.
     * In particular, make sure to drop all dependencies from the original build rule params,
     * as these are modeled via {@link CxxPreprocessAndCompile}.
     */
    public static HeaderSymlinkTree createHeaderSymlinkTreeBuildRule(SourcePathResolver resolver,
            BuildTarget target, BuildRuleParams params, Path root, ImmutableMap<Path, SourcePath> links,
            HeaderMode headerMode) {
        // Symlink trees never need to depend on anything.
        BuildRuleParams paramsWithoutDeps = params.copyWithChanges(target,
                Suppliers.ofInstance(ImmutableSortedSet.of()), Suppliers.ofInstance(ImmutableSortedSet.of()));

        switch (headerMode) {
        case SYMLINK_TREE_WITH_HEADER_MAP:
            return new HeaderSymlinkTreeWithHeaderMap(paramsWithoutDeps, resolver, root, links);
        case HEADER_MAP_ONLY:
            return new DirectHeaderMap(paramsWithoutDeps, resolver, root, links);
        default:
        case SYMLINK_TREE_ONLY:
            return new HeaderSymlinkTree(paramsWithoutDeps, resolver, root, links);
        }
    }

    /**
     * @return adds a the header {@link com.facebook.buck.rules.SymlinkTree} for the given rule to
     *     the {@link CxxPreprocessorInput}.
     */
    public static CxxPreprocessorInput.Builder addHeaderSymlinkTree(CxxPreprocessorInput.Builder builder,
            BuildTarget target, BuildRuleResolver ruleResolver, CxxPlatform platform,
            HeaderVisibility headerVisibility, IncludeType includeType) throws NoSuchBuildTargetException {
        BuildRule rule = ruleResolver.requireRule(BuildTarget.builder(target).addFlavors(platform.getFlavor(),
                CxxDescriptionEnhancer.getHeaderSymlinkTreeFlavor(headerVisibility)).build());
        Preconditions.checkState(rule instanceof HeaderSymlinkTree,
                "Attempt to add %s of type %s and class %s to %s", rule.getFullyQualifiedName(), rule.getType(),
                rule.getClass().getName(), target);
        HeaderSymlinkTree symlinkTree = (HeaderSymlinkTree) rule;
        builder.addIncludes(CxxSymlinkTreeHeaders.from(symlinkTree, includeType));
        return builder;
    }

    /**
     * @return The BuildRule corresponding to the exported (public) header symlink
     * tree for the provided target.
     */
    public static HeaderSymlinkTree requireHeaderSymlinkTreeForLibraryTarget(BuildRuleResolver ruleResolver,
            BuildTarget libraryBuildTarget, Flavor platformFlavor) {
        BuildRule rule;
        try {
            rule = ruleResolver
                    .requireRule(BuildTarget.builder(libraryBuildTarget)
                            .addFlavors(platformFlavor,
                                    CxxDescriptionEnhancer.getHeaderSymlinkTreeFlavor(HeaderVisibility.PUBLIC))
                            .build());
        } catch (NoSuchBuildTargetException e) {
            // This shouldn't happen; if a library rule exists, its header symlink tree rule
            // should exist.
            throw new IllegalStateException(e);
        }
        Preconditions.checkState(rule instanceof HeaderSymlinkTree);
        return (HeaderSymlinkTree) rule;
    }

    /**
     * Builds a {@link CxxPreprocessorInput} for a rule.
     */
    public static CxxPreprocessorInput getCxxPreprocessorInput(BuildRuleParams params,
            BuildRuleResolver ruleResolver, boolean hasHeaderSymlinkTree, CxxPlatform platform,
            HeaderVisibility headerVisibility, IncludeType includeType,
            Multimap<CxxSource.Type, String> exportedPreprocessorFlags, Iterable<FrameworkPath> frameworks)
            throws NoSuchBuildTargetException {
        CxxPreprocessorInput.Builder builder = CxxPreprocessorInput.builder();
        if (hasHeaderSymlinkTree) {
            addHeaderSymlinkTree(builder, params.getBuildTarget(), ruleResolver, platform, headerVisibility,
                    includeType);
        }
        return builder.putAllPreprocessorFlags(exportedPreprocessorFlags).addAllFrameworks(frameworks).build();
    }

    public static LoadingCache<CxxPreprocessorInputCacheKey, ImmutableMap<BuildTarget, CxxPreprocessorInput>> getTransitiveCxxPreprocessorInputCache(
            final CxxPreprocessorDep preprocessorDep) {
        return CacheBuilder.newBuilder().build(
                new CacheLoader<CxxPreprocessorInputCacheKey, ImmutableMap<BuildTarget, CxxPreprocessorInput>>() {
                    @Override
                    public ImmutableMap<BuildTarget, CxxPreprocessorInput> load(
                            @Nonnull CxxPreprocessorInputCacheKey key) throws Exception {
                        Map<BuildTarget, CxxPreprocessorInput> builder = new LinkedHashMap<>();
                        builder.put(preprocessorDep.getBuildTarget(),
                                preprocessorDep.getCxxPreprocessorInput(key.getPlatform(), key.getVisibility()));
                        for (CxxPreprocessorDep dep : preprocessorDep.getCxxPreprocessorDeps(key.getPlatform())) {
                            builder.putAll(
                                    dep.getTransitiveCxxPreprocessorInput(key.getPlatform(), key.getVisibility()));
                        }
                        return ImmutableMap.copyOf(builder);
                    }
                });
    }

    @Value.Immutable
    public abstract static class CxxPreprocessorInputCacheKey implements Comparable<CxxPreprocessorInputCacheKey> {

        @Value.Parameter
        public abstract CxxPlatform getPlatform();

        @Value.Parameter
        public abstract HeaderVisibility getVisibility();

        @Override
        public int compareTo(@Nonnull CxxPreprocessorInputCacheKey o) {
            return ComparisonChain.start().compare(getPlatform().getFlavor(), o.getPlatform().getFlavor())
                    .compare(getVisibility(), o.getVisibility()).result();
        }

    }

}