blob: 443dfc2f217a0f5d79569442a24d3f853f1ef0c2 [file] [log] [blame]
// Copyright 2023 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.analysis.producers;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.ConfiguredTargetValue;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.skyframe.ConfiguredValueCreationException;
import com.google.devtools.build.lib.skyframe.PackageValue;
import com.google.devtools.build.lib.skyframe.toolchains.PlatformLookupUtil;
import com.google.devtools.build.lib.skyframe.toolchains.PlatformLookupUtil.InvalidPlatformException;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.state.StateMachine;
import javax.annotation.Nullable;
/**
* Retrieves {@link PlatformInfo} for a given platform key.
*
* <p>This creates an explicit dependency on the {@link Package} to retrieve the associated target,
* so it is possible to verify that {@link PlatformInfo} is an advertised provider before
* constructing the {@link ConfiguredTarget}.
*/
final class PlatformInfoProducer
implements StateMachine, StateMachine.ValueOrExceptionSink<NoSuchPackageException> {
interface ResultSink {
void acceptPlatformInfo(PlatformInfo info);
void acceptPlatformInfoError(InvalidPlatformException error);
}
// -------------------- Input --------------------
private final ConfiguredTargetKey platformKey;
// -------------------- Output --------------------
private final ResultSink sink;
// -------------------- Sequencing --------------------
private final StateMachine runAfter;
// -------------------- Internal State --------------------
private boolean passedValidation = false;
private ConfiguredTarget platform;
PlatformInfoProducer(ConfiguredTargetKey platformKey, ResultSink sink, StateMachine runAfter) {
this.platformKey = platformKey;
this.sink = sink;
this.runAfter = runAfter;
}
@Override
public StateMachine step(Tasks tasks) {
// Loads the Package first to verify the Target. The ConfiguredTarget should not be loaded
// until after verification. See https://github.com/bazelbuild/bazel/pull/10307.
//
// In distributed analysis, these packages will be duplicated across shards.
tasks.lookUp(
platformKey.getLabel().getPackageIdentifier(),
NoSuchPackageException.class,
(StateMachine.ValueOrExceptionSink<NoSuchPackageException>) this);
return this::lookupPlatform;
}
@Override
public void acceptValueOrException(
@Nullable SkyValue value, @Nullable NoSuchPackageException error) {
if (value != null) {
var pkg = ((PackageValue) value).getPackage();
try {
var label = platformKey.getLabel();
var target = pkg.getTarget(label.getName());
if (!PlatformLookupUtil.hasPlatformInfo(target)) {
sink.acceptPlatformInfoError(new InvalidPlatformException(label)); // validation failure
return;
}
} catch (NoSuchTargetException e) {
sink.acceptPlatformInfoError(new InvalidPlatformException(e));
return;
}
passedValidation = true;
return;
}
if (error != null) {
sink.acceptPlatformInfoError(new InvalidPlatformException(error));
return;
}
throw new IllegalArgumentException("both value and error were null");
}
private StateMachine lookupPlatform(Tasks tasks) {
if (!passedValidation) {
return runAfter;
}
tasks.lookUp(
platformKey, ConfiguredValueCreationException.class, this::acceptPlatformValueOrError);
return this::retrievePlatformInfo;
}
private void acceptPlatformValueOrError(
@Nullable SkyValue value, @Nullable ConfiguredValueCreationException error) {
if (value != null) {
var configuredTargetValue = (ConfiguredTargetValue) value;
this.platform = configuredTargetValue.getConfiguredTarget();
return;
}
if (error != null) {
sink.acceptPlatformInfoError(new InvalidPlatformException(platformKey.getLabel(), error));
return;
}
throw new IllegalArgumentException("both value and error were null");
}
private StateMachine retrievePlatformInfo(Tasks tasks) {
if (platform == null) {
return runAfter; // An error occurred and was reported.
}
PlatformInfo platformInfo = PlatformProviderUtils.platform(platform);
if (platformInfo == null) {
sink.acceptPlatformInfoError(new InvalidPlatformException(platform.getLabel()));
return runAfter;
}
sink.acceptPlatformInfo(platformInfo);
return runAfter;
}
}