blob: 7b4ddd20e8751149b0ae871a7b85bafa024e7117 [file] [log] [blame]
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.skyframe;
import static java.util.stream.Collectors.joining;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.skyframe.CycleInfo;
import com.google.devtools.build.skyframe.CyclesReporter;
import com.google.devtools.build.skyframe.SkyKey;
import java.util.Optional;
/**
* {@link CyclesReporter.SingleCycleReporter} implementation that can handle cycles involving
* registered toolchains.
*/
public class RegisteredToolchainsCycleReporter implements CyclesReporter.SingleCycleReporter {
private static final Predicate<SkyKey> IS_REGISTERED_TOOLCHAINS_SKY_KEY =
SkyFunctions.isSkyFunction(SkyFunctions.REGISTERED_TOOLCHAINS);
private static final Predicate<SkyKey> IS_CONFIGURED_TARGET_SKY_KEY =
SkyFunctions.isSkyFunction(SkyFunctions.CONFIGURED_TARGET);
private static final Predicate<SkyKey> IS_SINGLE_TOOLCHAIN_RESOLUTION_SKY_KEY =
SkyFunctions.isSkyFunction(SkyFunctions.SINGLE_TOOLCHAIN_RESOLUTION);
private static final Predicate<SkyKey> IS_TOOLCHAIN_RESOLUTION_SKY_KEY =
SkyFunctions.isSkyFunction(SkyFunctions.TOOLCHAIN_RESOLUTION);
private static final Predicate<SkyKey> IS_TOOLCHAIN_RELATED =
Predicates.or(
IS_REGISTERED_TOOLCHAINS_SKY_KEY,
IS_SINGLE_TOOLCHAIN_RESOLUTION_SKY_KEY,
IS_TOOLCHAIN_RESOLUTION_SKY_KEY);
@Override
public boolean maybeReportCycle(
SkyKey topLevelKey,
CycleInfo cycleInfo,
boolean alreadyReported,
ExtendedEventHandler eventHandler) {
ImmutableList<SkyKey> cycle = cycleInfo.getCycle();
if (alreadyReported) {
return true;
} else if (!Iterables.any(cycle, IS_TOOLCHAIN_RELATED)) {
return false;
}
// Find the ConfiguredTargetKey, this should tell the problem.
Optional<ConfiguredTargetKey> configuredTargetKey = findRootConfiguredTarget(cycle);
if (!configuredTargetKey.isPresent()) {
return false;
}
Function<SkyKey, String> printer =
input -> {
if (input.argument() instanceof ConfiguredTargetKey) {
Label label = ((ConfiguredTargetKey) input.argument()).getLabel();
return label.toString();
}
if (input.argument() instanceof RegisteredToolchainsValue.Key) {
return "RegisteredToolchains";
}
if (input.argument() instanceof SingleToolchainResolutionValue.Key) {
Label toolchainType =
((SingleToolchainResolutionValue.Key) input.argument()).toolchainTypeLabel();
return String.format("toolchain type %s", toolchainType);
}
if (input.argument() instanceof UnloadedToolchainContext.Key) {
ImmutableSet<Label> toolchainTypes =
((UnloadedToolchainContext.Key) input.argument()).requiredToolchainTypeLabels();
return String.format(
"toolchain types %s",
toolchainTypes.stream().map(Label::toString).collect(joining(", ")));
}
throw new UnsupportedOperationException();
};
StringBuilder cycleMessage =
new StringBuilder()
.append("Misconfigured toolchains: ")
.append(printer.apply(configuredTargetKey.get()))
.append(" is declared as a toolchain but has inappropriate dependencies.")
.append(" Declared toolchains should be created with the 'toolchain' rule")
.append(" and should not have dependencies that themselves require toolchains.");
AbstractLabelCycleReporter.printCycle(cycleInfo.getCycle(), cycleMessage, printer);
eventHandler.handle(Event.error(null, cycleMessage.toString()));
return true;
}
/**
* Returns the first {@link SkyKey} that is an instance of {@link ConfiguredTargetKey} and follows
* {@link RegisteredToolchainsValue.Key}. This will loop over the cycle in case the {@link
* RegisteredToolchainsValue} is not first in the list.
*/
private Optional<ConfiguredTargetKey> findRootConfiguredTarget(ImmutableList<SkyKey> cycle) {
// Loop over the cycle, possibly twice, first looking for RegisteredToolchainsValue,
// then finding the first ConfiguredTargetKey.
boolean rtvFound = false;
for (int i = 0; i < cycle.size() * 2; i++) {
SkyKey skyKey = cycle.get(i % cycle.size());
if (!rtvFound && IS_REGISTERED_TOOLCHAINS_SKY_KEY.apply(skyKey)) {
rtvFound = true;
}
if (rtvFound && IS_CONFIGURED_TARGET_SKY_KEY.apply(skyKey)) {
return Optional.of((ConfiguredTargetKey) skyKey);
}
}
return Optional.empty();
}
}