blob: 6676a18321f5c69b81f818fcd8904bb87b9e27d7 [file] [log] [blame]
// Copyright 2017 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 com.google.auto.value.AutoValue;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.PlatformConfiguration;
import com.google.devtools.build.lib.analysis.ToolchainContext;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.ConfiguredValueCreationException;
import com.google.devtools.build.lib.skyframe.RegisteredToolchainsFunction.InvalidToolchainLabelException;
import com.google.devtools.build.lib.skyframe.ToolchainResolutionFunction.NoToolchainFoundException;
import com.google.devtools.build.lib.skyframe.ToolchainResolutionValue.ToolchainResolutionKey;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.skyframe.LegacySkyKey;
import com.google.devtools.build.skyframe.SkyFunction.Environment;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.ValueOrException;
import com.google.devtools.build.skyframe.ValueOrException4;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Common code to create a {@link ToolchainContext} given a set of required toolchain type labels.
*/
public class ToolchainUtil {
/**
* Returns a new {@link ToolchainContext}, with the correct toolchain labels based on the results
* of the {@link ToolchainResolutionFunction}.
*/
@Nullable
public static ToolchainContext createToolchainContext(
Environment env,
String targetDescription,
Set<Label> requiredToolchains,
BuildConfiguration configuration)
throws ToolchainContextException, InterruptedException {
ImmutableBiMap<Label, Label> resolvedLabels =
resolveToolchainLabels(env, requiredToolchains, configuration);
if (resolvedLabels == null) {
return null;
}
ToolchainContext toolchainContext =
ToolchainContext.create(targetDescription, requiredToolchains, resolvedLabels);
return toolchainContext;
}
/**
* Data class to hold platform descriptors loaded based on the current {@link BuildConfiguration}.
*/
@AutoValue
protected abstract static class PlatformDescriptors {
abstract PlatformInfo execPlatform();
abstract PlatformInfo targetPlatform();
protected static PlatformDescriptors create(
PlatformInfo execPlatform, PlatformInfo targetPlatform) {
return new AutoValue_ToolchainUtil_PlatformDescriptors(execPlatform, targetPlatform);
}
}
/**
* Returns the {@link PlatformInfo} provider from the {@link ConfiguredTarget} in the {@link
* ValueOrException}, or {@code null} if the {@link ConfiguredTarget} is not present. If the
* {@link ConfiguredTarget} does not have a {@link PlatformInfo} provider, a {@link
* InvalidPlatformException} is thrown, wrapped in a {@link ToolchainContextException}.
*/
@Nullable
private static PlatformInfo findPlatformInfo(
ValueOrException<ConfiguredValueCreationException> valueOrException, String platformType)
throws ConfiguredValueCreationException, ToolchainContextException {
ConfiguredTargetValue ctv = (ConfiguredTargetValue) valueOrException.get();
if (ctv == null) {
return null;
}
ConfiguredTarget configuredTarget = ctv.getConfiguredTarget();
PlatformInfo platformInfo = PlatformProviderUtils.platform(configuredTarget);
if (platformInfo == null) {
throw new ToolchainContextException(
new InvalidPlatformException(platformType, configuredTarget));
}
return platformInfo;
}
@Nullable
private static PlatformDescriptors loadPlatformDescriptors(
Environment env, BuildConfiguration configuration)
throws InterruptedException, ToolchainContextException {
PlatformConfiguration platformConfiguration =
configuration.getFragment(PlatformConfiguration.class);
Label executionPlatformLabel = platformConfiguration.getExecutionPlatform();
Label targetPlatformLabel = platformConfiguration.getTargetPlatforms().get(0);
SkyKey executionPlatformKey =
LegacySkyKey.create(
SkyFunctions.CONFIGURED_TARGET,
new ConfiguredTargetKey(executionPlatformLabel, configuration));
SkyKey targetPlatformKey =
LegacySkyKey.create(
SkyFunctions.CONFIGURED_TARGET,
new ConfiguredTargetKey(targetPlatformLabel, configuration));
Map<SkyKey, ValueOrException<ConfiguredValueCreationException>> values =
env.getValuesOrThrow(
ImmutableList.of(executionPlatformKey, targetPlatformKey),
ConfiguredValueCreationException.class);
boolean valuesMissing = env.valuesMissing();
try {
PlatformInfo execPlatform =
findPlatformInfo(values.get(executionPlatformKey), "execution platform");
PlatformInfo targetPlatform =
findPlatformInfo(values.get(targetPlatformKey), "target platform");
if (valuesMissing) {
return null;
}
return PlatformDescriptors.create(execPlatform, targetPlatform);
} catch (ConfiguredValueCreationException e) {
throw new ToolchainContextException(e);
}
}
@Nullable
private static ImmutableBiMap<Label, Label> resolveToolchainLabels(
Environment env, Set<Label> requiredToolchains, BuildConfiguration configuration)
throws InterruptedException, ToolchainContextException {
// If there are no required toolchains, bail out early.
if (requiredToolchains.isEmpty()) {
return ImmutableBiMap.of();
}
// Load the execution and target platforms for the current configuration.
PlatformDescriptors platforms = loadPlatformDescriptors(env, configuration);
if (platforms == null) {
return null;
}
// Find the toolchains for the required toolchain types.
List<SkyKey> registeredToolchainKeys = new ArrayList<>();
for (Label toolchainType : requiredToolchains) {
registeredToolchainKeys.add(
ToolchainResolutionValue.key(
configuration, toolchainType, platforms.targetPlatform(), platforms.execPlatform()));
}
Map<
SkyKey,
ValueOrException4<
NoToolchainFoundException, ConfiguredValueCreationException,
InvalidToolchainLabelException, EvalException>>
results =
env.getValuesOrThrow(
registeredToolchainKeys,
NoToolchainFoundException.class,
ConfiguredValueCreationException.class,
InvalidToolchainLabelException.class,
EvalException.class);
boolean valuesMissing = false;
// Load the toolchains.
ImmutableBiMap.Builder<Label, Label> builder = new ImmutableBiMap.Builder<>();
List<Label> missingToolchains = new ArrayList<>();
for (Map.Entry<
SkyKey,
ValueOrException4<
NoToolchainFoundException, ConfiguredValueCreationException,
InvalidToolchainLabelException, EvalException>>
entry : results.entrySet()) {
try {
Label requiredToolchainType =
((ToolchainResolutionKey) entry.getKey().argument()).toolchainType();
ValueOrException4<
NoToolchainFoundException, ConfiguredValueCreationException,
InvalidToolchainLabelException, EvalException>
valueOrException = entry.getValue();
if (valueOrException.get() == null) {
valuesMissing = true;
} else {
Label toolchainLabel =
((ToolchainResolutionValue) valueOrException.get()).toolchainLabel();
builder.put(requiredToolchainType, toolchainLabel);
}
} catch (NoToolchainFoundException e) {
// Save the missing type and continue looping to check for more.
missingToolchains.add(e.missingToolchainType());
} catch (ConfiguredValueCreationException e) {
throw new ToolchainContextException(e);
} catch (InvalidToolchainLabelException e) {
throw new ToolchainContextException(e);
} catch (EvalException e) {
throw new ToolchainContextException(e);
}
}
if (!missingToolchains.isEmpty()) {
throw new ToolchainContextException(new UnresolvedToolchainsException(missingToolchains));
}
if (valuesMissing) {
return null;
}
return builder.build();
}
/** Exception used when a platform label is not a valid platform. */
public static final class InvalidPlatformException extends Exception {
public InvalidPlatformException(String platformType, ConfiguredTarget resolvedTarget) {
super(
String.format(
"Target %s was found as the %s, but does not provide PlatformInfo",
resolvedTarget.getTarget(), platformType));
}
}
/** Exception used when a toolchain type is required but no matching toolchain is found. */
public static final class UnresolvedToolchainsException extends Exception {
private final ImmutableList<Label> missingToolchainTypes;
public UnresolvedToolchainsException(List<Label> missingToolchainTypes) {
super(
String.format(
"no matching toolchains found for types %s",
Joiner.on(", ").join(missingToolchainTypes)));
this.missingToolchainTypes = ImmutableList.copyOf(missingToolchainTypes);
}
public ImmutableList<Label> missingToolchainTypes() {
return missingToolchainTypes;
}
}
/** Exception used to wrap exceptions during toolchain resolution. */
public static class ToolchainContextException extends Exception {
public ToolchainContextException(InvalidPlatformException e) {
super(e);
}
public ToolchainContextException(UnresolvedToolchainsException e) {
super(e);
}
public ToolchainContextException(ConfiguredValueCreationException e) {
super(e);
}
public ToolchainContextException(InvalidToolchainLabelException e) {
super(e);
}
public ToolchainContextException(EvalException e) {
super(e);
}
}
}