Java tutorial
/* * Copyright 2018-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.cli; import com.facebook.buck.command.config.BuildBuckConfig; import com.facebook.buck.core.model.BuildTarget; import com.facebook.buck.core.model.actiongraph.computation.ActionGraphCache; import com.facebook.buck.core.model.actiongraph.computation.ActionGraphFactory; import com.facebook.buck.core.model.actiongraph.computation.ActionGraphProvider; import com.facebook.buck.core.model.targetgraph.TargetGraph; import com.facebook.buck.core.model.targetgraph.TargetGraphAndBuildTargets; import com.facebook.buck.core.rules.ActionGraphBuilder; import com.facebook.buck.core.rules.BuildRule; import com.facebook.buck.core.rules.SourcePathRuleFinder; import com.facebook.buck.core.rules.attr.HasRuntimeDeps; import com.facebook.buck.core.util.graph.AbstractBreadthFirstTraversal; import com.facebook.buck.event.ConsoleEvent; import com.facebook.buck.parser.SpeculativeParsing; import com.facebook.buck.parser.exceptions.BuildFileParseException; import com.facebook.buck.rules.modern.tools.IsolationChecker; import com.facebook.buck.rules.modern.tools.IsolationChecker.FailureReporter; import com.facebook.buck.util.CommandLineException; import com.facebook.buck.util.ExitCode; import com.facebook.buck.util.MoreExceptions; import com.facebook.buck.util.exceptions.BuckUncheckedExecutionException; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.TreeMultimap; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.stream.Collectors; import org.kohsuke.args4j.Argument; /** * Generates an isolation report. This report includes information indicating which nodes in the * action graph are isolateable (e.g. able to run in remote execution) and the requirements to do * that (absolute paths needed, toolchains needed, etc). */ public class AuditMbrIsolationCommand extends AbstractCommand { @Argument private List<String> arguments = new ArrayList<>(); public List<String> getArguments() { return arguments; } @Override public ExitCode runWithoutHelp(CommandRunnerParams params) { try { // Create a TargetGraph that is composed of the transitive closure of all of the dependent // BuildRules for the specified BuildTargetPaths. ImmutableSet<BuildTarget> targets = convertArgumentsToBuildTargets(params, getArguments()); if (targets.isEmpty()) { throw new CommandLineException("must specify at least one build target"); } TargetGraph targetGraph; try (CommandThreadManager pool = new CommandThreadManager("Audit", getConcurrencyLimit(params.getBuckConfig()))) { targetGraph = params.getParser() .buildTargetGraph(createParsingContext(params.getCell(), pool.getListeningExecutorService()) .withSpeculativeParsing(SpeculativeParsing.ENABLED) .withExcludeUnsupportedTargets(false), targets); } catch (BuildFileParseException e) { params.getBuckEventBus() .post(ConsoleEvent.severe(MoreExceptions.getHumanReadableOrLocalizedMessage(e))); return ExitCode.PARSE_ERROR; } if (params.getBuckConfig().getView(BuildBuckConfig.class).getBuildVersions()) { targetGraph = toVersionedTargetGraph(params, TargetGraphAndBuildTargets.of(targetGraph, targets)) .getTargetGraph(); } ActionGraphBuilder graphBuilder = Objects.requireNonNull(new ActionGraphProvider( params.getBuckEventBus(), ActionGraphFactory.create(params.getBuckEventBus(), params.getCell().getCellProvider(), params.getPoolSupplier(), params.getBuckConfig()), new ActionGraphCache( params.getBuckConfig().getView(BuildBuckConfig.class).getMaxActionGraphCacheEntries()), params.getRuleKeyConfiguration(), params.getBuckConfig()).getFreshActionGraph(targetGraph)) .getActionGraphBuilder(); graphBuilder.requireAllRules(targets); SerializationReportGenerator reportGenerator = new SerializationReportGenerator(); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); IsolationChecker isolationChecker = new IsolationChecker(ruleFinder, params.getCell().getCellPathResolver(), reportGenerator.getFailureReporter()); AbstractBreadthFirstTraversal.<BuildRule>traverse( targets.stream().map(graphBuilder::getRule).collect(Collectors.toList()), rule -> { isolationChecker.check(rule); ImmutableList.Builder<BuildRule> depsBuilder = ImmutableList.builder(); depsBuilder.addAll(rule.getBuildDeps()); if (rule instanceof HasRuntimeDeps) { depsBuilder.addAll(graphBuilder.getAllRules(((HasRuntimeDeps) rule) .getRuntimeDeps(ruleFinder).collect(Collectors.toList()))); } return depsBuilder.build(); }); String report = Joiner.on("\n").join(reportGenerator.generate()); params.getConsole().getStdOut().println(report); } catch (Exception e) { throw new BuckUncheckedExecutionException(e, "When inspecting serialization state of the action graph."); } return ExitCode.SUCCESS; } private static List<Entry<String, Collection<String>>> asSortedEntries(Multimap<String, String> failure) { return failure.asMap().entrySet().stream().sorted(Comparator.comparing(e -> -e.getValue().size())) .collect(Collectors.toList()); } @Override public boolean isReadOnly() { return true; } @Override public String getShortDescription() { return "provides facilities to audit build targets' classpaths"; } private static class SerializationReportGenerator { // Maps a rule type to a set of failures for that rule type. The set of failures in turn is a // map of failure message to a set of targets that failed in that way. Map<String, Multimap<String, String>> failuresByRuleType = new HashMap<>(); Map<String, Multimap<String, String>> absolutePathsRequired = new HashMap<>(); Multimap<String, String> successByType = ArrayListMultimap.create(); Multimap<String, String> notMigratedByType = ArrayListMultimap.create(); private FailureReporter failureReporter = new FailureReporter() { @Override public void reportNotMbr(BuildRule instance) { if (instance.hasBuildSteps()) { notMigratedByType.put(getRuleTypeString(instance), instance.getFullyQualifiedName()); } } @Override public void reportSerializationFailure(BuildRule instance, String crumbs, String message) { String error = String.format("%s %s", crumbs, message); Multimap<String, String> failedTargetsByMessage = failuresByRuleType .computeIfAbsent(getRuleTypeString(instance), ignored -> TreeMultimap.create()); failedTargetsByMessage.put(error, instance.getFullyQualifiedName()); } @Override public void reportAbsolutePath(BuildRule instance, String crumbs, Path path) { Multimap<String, String> inner = absolutePathsRequired.computeIfAbsent(path.toString(), ignored -> TreeMultimap.create()); inner.put(crumbs, instance.getFullyQualifiedName()); } @Override public void reportSuccess(BuildRule instance) { successByType.put(getRuleTypeString(instance), instance.getFullyQualifiedName()); } public String getRuleTypeString(BuildRule instance) { return instance.getType(); } }; public FailureReporter getFailureReporter() { return failureReporter; } public List<String> generate() { ReportBuilder builder = new ReportBuilder(); if (successByType.isEmpty()) { builder.addLine("No rules are serializable."); } else { for (Entry<String, Collection<String>> instance : asSortedEntries(successByType)) { builder.addLine("%s instances of %s are serializable.", instance.getValue().size(), instance.getKey()); } } builder.addSeparator(); if (notMigratedByType.isEmpty()) { builder.addLine("All used rules are migrated to ModernBuildRule."); } else { for (Entry<String, Collection<String>> instance : asSortedEntries(notMigratedByType)) { builder.addLine("%s instances of %s which is not yet migrated to ModernBuildRule.", instance.getValue().size(), instance.getKey()); } } builder.addSeparator(); if (failuresByRuleType.isEmpty()) { builder.addLine("There's no failures for rules migrated to ModernBuildRule."); } else { for (Map.Entry<String, Multimap<String, String>> failure : failuresByRuleType.entrySet().stream() .sorted(Comparator.comparing(entry -> -entry.getValue().size())) .collect(Collectors.toList())) { builder.addLine("%s failures for rules of type %s.", failure.getValue().size(), failure.getKey()); for (Entry<String, Collection<String>> instance : asSortedEntries(failure.getValue())) { builder.addLine(" %s: %s", instance.getValue().size(), instance.getKey()); int count = 0; int max = 3; for (String target : instance.getValue()) { if (count >= max) { builder.addLine(" ..."); break; } builder.addLine(" %s", target); count++; } } } } builder.addSeparator(); if (absolutePathsRequired.isEmpty()) { builder.addLine("Didn't find any references to absolute paths."); } else { for (Map.Entry<String, Multimap<String, String>> requiredPath : absolutePathsRequired.entrySet() .stream().sorted(Comparator.comparing(entry -> -entry.getValue().size())) .collect(Collectors.toList())) { builder.addLine("%s referenced by %s rules.", requiredPath.getKey(), requiredPath.getValue().size()); for (Entry<String, Collection<String>> instance : asSortedEntries(requiredPath.getValue())) { builder.addLine(" %s: %s", instance.getValue().size(), instance.getKey()); int count = 0; int max = 3; for (String target : instance.getValue()) { if (count >= max) { builder.addLine(" ..."); break; } builder.addLine(" %s", target); count++; } } } } return builder.reportLines; } private static class ReportBuilder { List<String> reportLines = new ArrayList<>(); private void addSeparator() { reportLines.add("-------------------------------------------------------------"); reportLines.add("-------------------------------------------------------------"); reportLines.add("-------------------------------------------------------------"); } private void addLine(String message) { reportLines.add(message); } private void addLine(String format, Object... args) { reportLines.add(String.format(format, args)); } } } }