com.facebook.buck.jvm.java.DefaultSuggestBuildRules.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.jvm.java.DefaultSuggestBuildRules.java

Source

/*
 * Copyright 2012-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.jvm.java;

import com.facebook.buck.graph.DirectedAcyclicGraph;
import com.facebook.buck.graph.TopologicalSort;
import com.facebook.buck.jvm.core.SuggestBuildRules;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleDependencyVisitors;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;

import java.nio.file.Path;
import java.util.Set;

final class DefaultSuggestBuildRules implements SuggestBuildRules {
    private final Supplier<ImmutableList<JavaLibrary>> sortedTransitiveNotDeclaredDeps;
    private final JarResolver jarResolver;

    /**
     * @return A function that takes a list of failed imports from a javac invocation and returns a
     *    set of rules to suggest that the developer import to satisfy those imports.
     */
    static SuggestBuildRules createSuggestBuildFunction(final JarResolver jarResolver,
            final ImmutableSet<JavaLibrary> declaredDeps, final ImmutableSet<JavaLibrary> transitiveDeps,
            final Iterable<BuildRule> buildRules) {

        final Supplier<ImmutableList<JavaLibrary>> sortedTransitiveNotDeclaredDeps = Suppliers
                .memoize(new Supplier<ImmutableList<JavaLibrary>>() {
                    @Override
                    public ImmutableList<JavaLibrary> get() {
                        Set<JavaLibrary> transitiveNotDeclaredDeps = Sets.difference(transitiveDeps,
                                Sets.union(ImmutableSet.of(this), declaredDeps));
                        DirectedAcyclicGraph<BuildRule> graph = BuildRuleDependencyVisitors
                                .getBuildRuleDirectedGraphFilteredBy(buildRules, JavaLibrary.class::isInstance,
                                        JavaLibrary.class::isInstance);
                        return FluentIterable.from(TopologicalSort.sort(graph)).filter(JavaLibrary.class)
                                .filter(transitiveNotDeclaredDeps::contains).toList().reverse();
                    }
                });

        return new DefaultSuggestBuildRules(sortedTransitiveNotDeclaredDeps, jarResolver);
    }

    @Override
    public ImmutableSet<String> suggest(ImmutableSet<String> failedImports) {
        ImmutableSet.Builder<String> suggestedDeps = ImmutableSet.builder();

        Set<String> remainingImports = Sets.newHashSet(failedImports);

        for (JavaLibrary transitiveNotDeclaredDep : sortedTransitiveNotDeclaredDeps.get()) {
            if (isMissingBuildRule(transitiveNotDeclaredDep, remainingImports, jarResolver)) {
                suggestedDeps.add(transitiveNotDeclaredDep.getFullyQualifiedName());
            }
            // If we've wiped out all remaining imports, break the loop looking for them.
            if (remainingImports.isEmpty()) {
                break;
            }
        }
        return suggestedDeps.build();
    }

    private DefaultSuggestBuildRules(Supplier<ImmutableList<JavaLibrary>> sortedTransitiveNotDeclaredDeps,
            JarResolver jarResolver) {
        this.sortedTransitiveNotDeclaredDeps = sortedTransitiveNotDeclaredDeps;
        this.jarResolver = jarResolver;
    }

    /**
     *  @param transitiveNotDeclaredRule A {@link BuildRule} that is contained in the transitive
     *      dependency list but is not declared as a dependency.
     *  @param failedImports A Set of remaining failed imports.  This function will mutate this set
     *      and remove any imports satisfied by {@code transitiveNotDeclaredDep}.
     *  @return whether or not adding {@code transitiveNotDeclaredDep} as a dependency to this build
     *      rule would have satisfied one of the {@code failedImports}.
     */
    private boolean isMissingBuildRule(BuildRule transitiveNotDeclaredRule, Set<String> failedImports,
            JarResolver jarResolver) {
        if (!(transitiveNotDeclaredRule instanceof JavaLibrary)) {
            return false;
        }

        ImmutableSet<Path> classPaths = ((JavaLibrary) transitiveNotDeclaredRule).getOutputClasspaths();
        boolean containsMissingBuildRule = false;
        // Open the output jar for every jar contained as the output of transitiveNotDeclaredDep.  With
        // the exception of rules that export their dependencies, this will result in a single
        // classpath.
        for (Path classPath : classPaths) {
            ImmutableSet<String> topLevelSymbols = jarResolver.resolve(classPath);

            for (String symbolName : topLevelSymbols) {
                if (failedImports.contains(symbolName)) {
                    failedImports.remove(symbolName);
                    containsMissingBuildRule = true;

                    // If we've found all of the missing imports, bail out early.
                    if (failedImports.isEmpty()) {
                        return true;
                    }
                }
            }
        }
        return containsMissingBuildRule;
    }
}