blob: 301774d13ae64287c54bd257ab2420059ced5c00 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.skyframe;
15
tomlua155b532017-11-08 20:12:47 +010016import com.google.common.base.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.common.collect.ImmutableList;
Klaus Aehlig777b30d2017-02-24 16:30:15 +000018import com.google.devtools.build.lib.events.ExtendedEventHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019
20/**
21 * An utility for custom reporting of errors from cycles in the the Skyframe graph. This class is
22 * stateful in order to differentiate between new cycles and cycles that have already been
23 * reported (do not reuse the instances or cache the results as it could end up printing
24 * inconsistent information or leak memory). It treats two cycles as the same if they contain the
25 * same {@link SkyKey}s in the same order, but perhaps with different starting points. See
26 * {@link CycleDeduper} for more information.
27 */
28public class CyclesReporter {
29
30 /**
31 * Interface for reporting custom information about a single cycle.
32 */
33 public interface SingleCycleReporter {
34
35 /**
Klaus Aehlig777b30d2017-02-24 16:30:15 +000036 * Reports the given cycle and returns {@code true}, or return {@code false} if this {@link
37 * SingleCycleReporter} doesn't know how to report the cycle.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038 *
39 * @param topLevelKey the top level key that transitively depended on the cycle
40 * @param cycleInfo the cycle
Klaus Aehlig777b30d2017-02-24 16:30:15 +000041 * @param alreadyReported whether the cycle has already been reported to the {@link
42 * CyclesReporter}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043 * @param eventHandler the eventHandler to which to report the error
44 */
Klaus Aehlig777b30d2017-02-24 16:30:15 +000045 boolean maybeReportCycle(
46 SkyKey topLevelKey,
47 CycleInfo cycleInfo,
48 boolean alreadyReported,
49 ExtendedEventHandler eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050 }
51
52 private final ImmutableList<SingleCycleReporter> cycleReporters;
53 private final CycleDeduper<SkyKey> cycleDeduper = new CycleDeduper<>();
54
55 /**
56 * Constructs a {@link CyclesReporter} that delegates to the given {@link SingleCycleReporter}s,
57 * in the given order, to report custom information about cycles.
58 */
59 public CyclesReporter(SingleCycleReporter... cycleReporters) {
60 this.cycleReporters = ImmutableList.copyOf(cycleReporters);
61 }
62
63 /**
64 * Reports the given cycles, differentiating between cycles that have already been reported.
65 *
66 * @param cycles The {@code Iterable} of cycles.
67 * @param topLevelKey This key represents the top level value key that returned cycle errors.
68 * @param eventHandler the eventHandler to which to report the error
69 */
Klaus Aehlig777b30d2017-02-24 16:30:15 +000070 public void reportCycles(
71 Iterable<CycleInfo> cycles, SkyKey topLevelKey, ExtendedEventHandler eventHandler) {
janakr20984742019-01-28 10:36:35 -080072 Preconditions.checkNotNull(eventHandler, "topLevelKey: %s, Cycles %s", topLevelKey, cycles);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073 for (CycleInfo cycleInfo : cycles) {
janakr20984742019-01-28 10:36:35 -080074 maybeReportCycle(cycleInfo, topLevelKey, eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075 }
76 }
77
janakr20984742019-01-28 10:36:35 -080078 private void maybeReportCycle(
79 CycleInfo cycleInfo, SkyKey topLevelKey, ExtendedEventHandler eventHandler) {
80 boolean alreadyReported = !cycleDeduper.seen(cycleInfo.getCycle());
81 for (SingleCycleReporter cycleReporter : cycleReporters) {
82 if (cycleReporter.maybeReportCycle(topLevelKey, cycleInfo, alreadyReported, eventHandler)) {
83 return;
84 }
85 }
86 throw new IllegalStateException(
87 printArbitraryCycle(topLevelKey, cycleInfo, alreadyReported) + "\n" + cycleReporters);
88 }
89
90 private static String printArbitraryCycle(
91 SkyKey topLevelKey, CycleInfo cycleInfo, boolean alreadyReported) {
92 StringBuilder cycleMessage =
93 new StringBuilder()
94 .append("topLevelKey: ")
95 .append(topLevelKey)
96 .append("\n")
97 .append("alreadyReported: ")
98 .append(alreadyReported)
99 .append("\n")
100 .append("path to cycle:\n");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101 for (SkyKey skyKey : cycleInfo.getPathToCycle()) {
janakr20984742019-01-28 10:36:35 -0800102 cycleMessage.append(skyKey).append("\n");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100103 }
104 cycleMessage.append("cycle:\n");
105 for (SkyKey skyKey : cycleInfo.getCycle()) {
janakr20984742019-01-28 10:36:35 -0800106 cycleMessage.append(skyKey).append("\n");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100107 }
108 return cycleMessage.toString();
109 }
110}