diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/BUILD index 995db81500ae10..43732b3db86375 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/BUILD @@ -218,7 +218,10 @@ java_library( java_library( name = "single_toolchain_resolution_function", - srcs = ["SingleToolchainResolutionFunction.java"], + srcs = [ + "SingleToolchainResolutionDebugPrinter.java", + "SingleToolchainResolutionFunction.java", + ], deps = [ ":platform_lookup_util", ":registered_toolchains_function", diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/SingleToolchainResolutionDebugPrinter.java b/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/SingleToolchainResolutionDebugPrinter.java new file mode 100644 index 00000000000000..4d966d98c8e9f7 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/SingleToolchainResolutionDebugPrinter.java @@ -0,0 +1,286 @@ +// Copyright 2024 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.toolchains; + +import static java.util.stream.Collectors.joining; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.analysis.platform.ConstraintCollection; +import com.google.devtools.build.lib.analysis.platform.ConstraintSettingInfo; +import com.google.devtools.build.lib.analysis.platform.PlatformInfo; +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.lib.skyframe.ConfiguredTargetKey; +import com.google.errorprone.annotations.FormatMethod; +import com.google.errorprone.annotations.FormatString; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** A helper interface for printing debug messages from single toolchain resolution. */ +public sealed interface SingleToolchainResolutionDebugPrinter { + static SingleToolchainResolutionDebugPrinter create( + boolean debug, ExtendedEventHandler eventHandler) { + if (debug) { + return new EventHandlerImpl(eventHandler); + } + return new NoopPrinter(); + } + + void finishDebugging(); + + void describeRejectedToolchains(ImmutableMap rejectedToolchains); + + void startToolchainResolution(Label toolchainType, Label targetPlatform); + + void reportCompatibleTargetPlatform(Label toolchainLabel); + + void reportSkippedExecutionPlatformSeen(Label executionPlatform); + + void reportSkippedExecutionPlatformDisallowed(Label executionPlatform, Label toolchainType); + + void reportCompatibleExecutionPlatform(Label executionPlatform); + + void reportResolvedToolchains( + ImmutableMap resolvedToolchains, + Label targetPlatform, + Label toolchainType); + + void reportMismatchedSettings( + ConstraintCollection toolchainConstraints, + boolean isTargetPlatform, + PlatformInfo platform, + Label toolchainLabel, + ImmutableSet mismatchSettingsWithDefault); + + void reportDone(Label toolchainType); + + /** A do-nothing implementation for when debug messages are suppressed. */ + final class NoopPrinter implements SingleToolchainResolutionDebugPrinter { + + private NoopPrinter() {} + + @Override + public void finishDebugging() {} + + @Override + public void describeRejectedToolchains(ImmutableMap rejectedToolchains) {} + + @Override + public void startToolchainResolution(Label toolchainType, Label targetPlatform) {} + + @Override + public void reportCompatibleTargetPlatform(Label toolchainLabel) {} + + @Override + public void reportSkippedExecutionPlatformSeen(Label executionPlatform) {} + + @Override + public void reportSkippedExecutionPlatformDisallowed( + Label executionPlatform, Label toolchainType) {} + + @Override + public void reportCompatibleExecutionPlatform(Label executionPlatform) {} + + @Override + public void reportResolvedToolchains( + ImmutableMap resolvedToolchains, + Label targetPlatform, + Label toolchainType) {} + + @Override + public void reportMismatchedSettings( + ConstraintCollection toolchainConstraints, + boolean isTargetPlatform, + PlatformInfo platform, + Label toolchainLabel, + ImmutableSet mismatchSettingsWithDefault) {} + + @Override + public void reportDone(Label toolchainType) {} + } + + /** Implement debug printing using the {@link ExtendedEventHandler}. */ + final class EventHandlerImpl implements SingleToolchainResolutionDebugPrinter { + /** Helper enum to define the three indentation levels used in {@code debugMessage}. */ + private enum IndentLevel { + TARGET_PLATFORM_LEVEL(""), + TOOLCHAIN_LEVEL(" "), + EXECUTION_PLATFORM_LEVEL(" "); + + final String value; + + IndentLevel(String value) { + this.value = value; + } + + public String indent() { + return value; + } + } + + private final ExtendedEventHandler eventHandler; + private final List resolutionTrace = new ArrayList<>(); + + private EventHandlerImpl(ExtendedEventHandler eventHandler) { + this.eventHandler = eventHandler; + } + + @FormatMethod + private void debugMessage(IndentLevel indent, @FormatString String template, Object... args) { + String padding = resolutionTrace.isEmpty() ? "" : " ".repeat("INFO: ".length()); + resolutionTrace.add( + padding + "ToolchainResolution: " + indent.indent() + String.format(template, args)); + } + + @Override + public void finishDebugging() { + eventHandler.handle(Event.info(String.join("\n", resolutionTrace))); + } + + @Override + public void describeRejectedToolchains(ImmutableMap rejectedToolchains) { + if (!rejectedToolchains.isEmpty()) { + for (Map.Entry entry : rejectedToolchains.entrySet()) { + Label toolchainLabel = entry.getKey(); + String message = entry.getValue(); + debugMessage( + IndentLevel.TOOLCHAIN_LEVEL, "Rejected toolchain %s; %s", toolchainLabel, message); + } + } + } + + @Override + public void startToolchainResolution(Label toolchainType, Label targetPlatform) { + debugMessage( + IndentLevel.TARGET_PLATFORM_LEVEL, + "Performing resolution of %s for target platform %s", + toolchainType, + targetPlatform); + } + + @Override + public void reportCompatibleTargetPlatform(Label toolchainLabel) { + debugMessage( + IndentLevel.TOOLCHAIN_LEVEL, + "Toolchain %s is compatible with target platform, searching for execution platforms:", + toolchainLabel); + } + + @Override + public void reportSkippedExecutionPlatformSeen(Label executionPlatform) { + debugMessage( + IndentLevel.EXECUTION_PLATFORM_LEVEL, + "Skipping execution platform %s; it has already selected a toolchain", + executionPlatform); + } + + @Override + public void reportSkippedExecutionPlatformDisallowed( + Label executionPlatform, Label toolchainType) { + debugMessage( + IndentLevel.EXECUTION_PLATFORM_LEVEL, + "Skipping execution platform %s; its allowed toolchain types does not contain the" + + " current toolchain type %s", + executionPlatform, + toolchainType); + } + + @Override + public void reportCompatibleExecutionPlatform(Label executionPlatform) { + debugMessage( + IndentLevel.EXECUTION_PLATFORM_LEVEL, + "Compatible execution platform %s", + executionPlatform); + } + + @Override + public void reportResolvedToolchains( + ImmutableMap resolvedToolchains, + Label targetPlatform, + Label toolchainType) { + if (resolvedToolchains.isEmpty()) { + debugMessage( + IndentLevel.TARGET_PLATFORM_LEVEL, + "No %s toolchain found for target platform %s.", + toolchainType, + targetPlatform); + } else { + debugMessage( + IndentLevel.TARGET_PLATFORM_LEVEL, + "Recap of selected %s toolchains for target platform %s:", + toolchainType, + targetPlatform); + resolvedToolchains.forEach( + (executionPlatformKey, toolchainLabel) -> + debugMessage( + IndentLevel.TOOLCHAIN_LEVEL, + "Selected %s to run on execution platform %s", + toolchainLabel, + executionPlatformKey.getLabel())); + } + } + + @Override + public void reportMismatchedSettings( + ConstraintCollection toolchainConstraints, + boolean isTargetPlatform, + PlatformInfo platform, + Label toolchainLabel, + ImmutableSet mismatchSettingsWithDefault) { + if (!mismatchSettingsWithDefault.isEmpty()) { + String mismatchValues = + mismatchSettingsWithDefault.stream() + .filter(toolchainConstraints::has) + .map(s -> toolchainConstraints.get(s).label().getName()) + .collect(joining(", ")); + if (!mismatchValues.isEmpty()) { + mismatchValues = "; mismatching values: " + mismatchValues; + } + + String missingSettings = + mismatchSettingsWithDefault.stream() + .filter(s -> !toolchainConstraints.has(s)) + .map(s -> s.label().getName()) + .collect(joining(", ")); + if (!missingSettings.isEmpty()) { + missingSettings = "; missing: " + missingSettings; + } + if (isTargetPlatform) { + debugMessage( + IndentLevel.TOOLCHAIN_LEVEL, + "Rejected toolchain %s%s", + toolchainLabel, + mismatchValues + missingSettings); + } else { + debugMessage( + IndentLevel.EXECUTION_PLATFORM_LEVEL, + "Incompatible execution platform %s%s", + platform.label(), + mismatchValues + missingSettings); + } + } + } + + @Override + public void reportDone(Label toolchainType) { + debugMessage( + IndentLevel.TOOLCHAIN_LEVEL, + "All execution platforms have been assigned a %s toolchain, stopping", + toolchainType); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/SingleToolchainResolutionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/SingleToolchainResolutionFunction.java index ce967154e799cc..d6ac7b6551ebeb 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/SingleToolchainResolutionFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/toolchains/SingleToolchainResolutionFunction.java @@ -15,7 +15,6 @@ package com.google.devtools.build.lib.skyframe.toolchains; import static com.google.common.collect.ImmutableList.toImmutableList; -import static java.util.stream.Collectors.joining; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -30,7 +29,6 @@ import com.google.devtools.build.lib.analysis.platform.PlatformInfo; import com.google.devtools.build.lib.analysis.platform.ToolchainTypeInfo; import com.google.devtools.build.lib.cmdline.Label; -import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.server.FailureDetails.Toolchain.Code; import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; import com.google.devtools.build.lib.skyframe.toolchains.PlatformLookupUtil.InvalidPlatformException; @@ -40,12 +38,10 @@ import com.google.devtools.build.skyframe.SkyFunctionException; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; -import com.google.errorprone.annotations.FormatMethod; -import com.google.errorprone.annotations.FormatString; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; @@ -88,40 +84,26 @@ public SkyValue compute(SkyKey skyKey, Environment env) throw new ToolchainResolutionFunctionException(e); } - List resolutionTrace = debug ? new ArrayList<>() : null; - - // If debugging, describe rejected toolchains. - if (debug - && toolchains.rejectedToolchains() != null - && !toolchains.rejectedToolchains().isEmpty()) { - ImmutableMap rejectedToolchainImplementations = - toolchains.rejectedToolchains().row(key.toolchainType().toolchainType()); - for (Map.Entry entry : rejectedToolchainImplementations.entrySet()) { - Label toolchainLabel = entry.getKey(); - String message = entry.getValue(); - debugMessage( - resolutionTrace, - IndentLevel.TOOLCHAIN_LEVEL, - "Rejected toolchain %s; %s", - toolchainLabel, - message); - } - } + SingleToolchainResolutionDebugPrinter debugPrinter = + SingleToolchainResolutionDebugPrinter.create(debug, env.getListener()); + + // Describe rejected toolchains if any are present. + Optional.ofNullable(toolchains.rejectedToolchains()) + .map(rejectedToolchains -> rejectedToolchains.row(key.toolchainType().toolchainType())) + .ifPresent(debugPrinter::describeRejectedToolchains); // Find the right one. SingleToolchainResolutionValue toolchainResolution = resolveConstraints( + env, + debugPrinter, key.toolchainType(), key.toolchainTypeInfo(), key.availableExecutionPlatformKeys(), key.targetPlatformKey(), - toolchains.registeredToolchains(), - env, - resolutionTrace); + toolchains.registeredToolchains()); - if (debug) { - env.getListener().handle(Event.info(String.join("\n", resolutionTrace))); - } + debugPrinter.finishDebugging(); return toolchainResolution; } @@ -133,13 +115,13 @@ public SkyValue compute(SkyKey skyKey, Environment env) */ @Nullable private static SingleToolchainResolutionValue resolveConstraints( + Environment env, + SingleToolchainResolutionDebugPrinter debugPrinter, ToolchainTypeRequirement toolchainType, ToolchainTypeInfo toolchainTypeInfo, List availableExecutionPlatformKeys, ConfiguredTargetKey targetPlatformKey, - ImmutableList toolchains, - Environment env, - @Nullable List resolutionTrace) + ImmutableList toolchains) throws ToolchainResolutionFunctionException, InterruptedException { // Load the PlatformInfo needed to check constraints. @@ -176,17 +158,12 @@ private static SingleToolchainResolutionValue resolveConstraints( toolchain.toolchainType().typeLabel().equals(toolchainType.toolchainType())) .collect(toImmutableList()); - debugMessage( - resolutionTrace, - IndentLevel.TARGET_PLATFORM_LEVEL, - "Performing resolution of %s for target platform %s", - toolchainType.toolchainType(), - targetPlatform.label()); + debugPrinter.startToolchainResolution(toolchainType.toolchainType(), targetPlatform.label()); for (DeclaredToolchainInfo toolchain : filteredToolchains) { // Make sure the target platform matches. if (!checkConstraints( - resolutionTrace, + debugPrinter, toolchain.targetConstraints(), /* isTargetPlatform= */ true, targetPlatform, @@ -194,11 +171,7 @@ private static SingleToolchainResolutionValue resolveConstraints( continue; } - debugMessage( - resolutionTrace, - IndentLevel.TOOLCHAIN_LEVEL, - "Toolchain %s is compatible with target platform, searching for execution platforms:", - toolchain.toolchainLabel()); + debugPrinter.reportCompatibleTargetPlatform(toolchain.toolchainLabel()); boolean done = true; @@ -206,11 +179,7 @@ private static SingleToolchainResolutionValue resolveConstraints( for (ConfiguredTargetKey executionPlatformKey : availableExecutionPlatformKeys) { // Only check the toolchains if this is a new platform. if (platformKeysSeen.contains(executionPlatformKey)) { - debugMessage( - resolutionTrace, - IndentLevel.EXECUTION_PLATFORM_LEVEL, - "Skipping execution platform %s; it has already selected a toolchain", - executionPlatformKey.getLabel()); + debugPrinter.reportSkippedExecutionPlatformSeen(executionPlatformKey.getLabel()); continue; } @@ -219,13 +188,8 @@ private static SingleToolchainResolutionValue resolveConstraints( // Check if the platform allows this toolchain type. if (executionPlatform.checkToolchainTypes() && !executionPlatform.allowedToolchainTypes().contains(toolchainType.toolchainType())) { - debugMessage( - resolutionTrace, - IndentLevel.EXECUTION_PLATFORM_LEVEL, - "Skipping execution platform %s; its allowed toolchain types does not contain the" - + " current toolchain type %s", - executionPlatformKey.getLabel(), - toolchainType.toolchainType()); + debugPrinter.reportSkippedExecutionPlatformDisallowed( + executionPlatformKey.getLabel(), toolchainType.toolchainType()); // Keep looking for a valid toolchain for this exec platform done = false; @@ -234,7 +198,7 @@ private static SingleToolchainResolutionValue resolveConstraints( // Check if the execution constraints match. if (!checkConstraints( - resolutionTrace, + debugPrinter, toolchain.execConstraints(), /* isTargetPlatform= */ false, executionPlatform, @@ -244,96 +208,30 @@ private static SingleToolchainResolutionValue resolveConstraints( continue; } - debugMessage( - resolutionTrace, - IndentLevel.EXECUTION_PLATFORM_LEVEL, - "Compatible execution platform %s", - executionPlatformKey.getLabel()); + debugPrinter.reportCompatibleExecutionPlatform(executionPlatformKey.getLabel()); builder.put(executionPlatformKey, toolchain.toolchainLabel()); platformKeysSeen.add(executionPlatformKey); } if (done) { - debugMessage( - resolutionTrace, - IndentLevel.TOOLCHAIN_LEVEL, - "All execution platforms have been assigned a %s toolchain, stopping", - toolchainType.toolchainType()); + debugPrinter.reportDone(toolchainType.toolchainType()); break; } } ImmutableMap resolvedToolchainLabels = builder.buildOrThrow(); - if (resolutionTrace != null) { - if (resolvedToolchainLabels.isEmpty()) { - debugMessage( - resolutionTrace, - IndentLevel.TARGET_PLATFORM_LEVEL, - "No %s toolchain found for target platform %s.", - toolchainType.toolchainType(), - targetPlatform.label()); - } else { - debugMessage( - resolutionTrace, - IndentLevel.TARGET_PLATFORM_LEVEL, - "Recap of selected %s toolchains for target platform %s:", - toolchainType.toolchainType(), - targetPlatform.label()); - resolvedToolchainLabels.forEach( - (executionPlatformKey, toolchainLabel) -> - debugMessage( - resolutionTrace, - IndentLevel.TOOLCHAIN_LEVEL, - "Selected %s to run on execution platform %s", - toolchainLabel, - executionPlatformKey.getLabel())); - } - } + debugPrinter.reportResolvedToolchains( + resolvedToolchainLabels, targetPlatform.label(), toolchainType.toolchainType()); return SingleToolchainResolutionValue.create(toolchainTypeInfo, resolvedToolchainLabels); } - /** Helper enum to define the three indentation levels used in {@code debugMessage}. */ - private enum IndentLevel { - TARGET_PLATFORM_LEVEL(""), - TOOLCHAIN_LEVEL(" "), - EXECUTION_PLATFORM_LEVEL(" "); - - final String value; - - IndentLevel(String value) { - this.value = value; - } - - public String indent() { - return value; - } - } - - /** - * Helper method to print a debugging message, if the given {@code resolutionTrace} is not {@code - * null}. - */ - @FormatMethod - private static void debugMessage( - @Nullable List resolutionTrace, - IndentLevel indent, - @FormatString String template, - Object... args) { - if (resolutionTrace == null) { - return; - } - String padding = resolutionTrace.isEmpty() ? "" : " ".repeat("INFO: ".length()); - resolutionTrace.add( - padding + "ToolchainResolution: " + indent.indent() + String.format(template, args)); - } - /** * Returns {@code true} iff all constraints set by the toolchain and in the {@link PlatformInfo} * match. */ private static boolean checkConstraints( - @Nullable List resolutionTrace, + SingleToolchainResolutionDebugPrinter debugPrinter, ConstraintCollection toolchainConstraints, boolean isTargetPlatform, PlatformInfo platform, @@ -356,40 +254,12 @@ private static boolean checkConstraints( .filter(toolchainConstraints::hasWithoutDefault) .collect(ImmutableSet.toImmutableSet()); - if (resolutionTrace != null && !mismatchSettingsWithDefault.isEmpty()) { - String mismatchValues = - mismatchSettingsWithDefault.stream() - .filter(toolchainConstraints::has) - .map(s -> toolchainConstraints.get(s).label().getName()) - .collect(joining(", ")); - if (!mismatchValues.isEmpty()) { - mismatchValues = "; mismatching values: " + mismatchValues; - } - - String missingSettings = - mismatchSettingsWithDefault.stream() - .filter(s -> !toolchainConstraints.has(s)) - .map(s -> s.label().getName()) - .collect(joining(", ")); - if (!missingSettings.isEmpty()) { - missingSettings = "; missing: " + missingSettings; - } - if (isTargetPlatform) { - debugMessage( - resolutionTrace, - IndentLevel.TOOLCHAIN_LEVEL, - "Rejected toolchain %s%s", - toolchainLabel, - mismatchValues + missingSettings); - } else { - debugMessage( - resolutionTrace, - IndentLevel.EXECUTION_PLATFORM_LEVEL, - "Incompatible execution platform %s%s", - platform.label(), - mismatchValues + missingSettings); - } - } + debugPrinter.reportMismatchedSettings( + toolchainConstraints, + isTargetPlatform, + platform, + toolchainLabel, + mismatchSettingsWithDefault); return mismatchSettingsWithDefault.isEmpty(); }