| // 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.analysis.producers; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.analysis.platform.PlatformInfo; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.cmdline.Label.PackageContext; |
| import com.google.devtools.build.lib.cmdline.RepositoryMapping; |
| import com.google.devtools.build.lib.skyframe.RepositoryMappingValue; |
| import com.google.devtools.build.lib.skyframe.config.NativeAndStarlarkFlags; |
| import com.google.devtools.build.lib.skyframe.config.ParsedFlagsValue; |
| 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 com.google.devtools.common.options.OptionsParsingException; |
| import javax.annotation.Nullable; |
| |
| /** Retrieves the parsed flags for a given platform. */ |
| public class PlatformFlagsProducer implements StateMachine, PlatformInfoProducer.ResultSink { |
| |
| interface ResultSink { |
| void acceptPlatformFlags(Label platform, NativeAndStarlarkFlags flags); |
| |
| void acceptPlatformFlagsError(Label platform, InvalidPlatformException error); |
| |
| void acceptPlatformFlagsError(Label platform, OptionsParsingException error); |
| } |
| |
| // -------------------- Input -------------------- |
| private final Label platformLabel; |
| |
| // -------------------- Output -------------------- |
| private final ResultSink sink; |
| |
| // -------------------- Sequencing -------------------- |
| private final StateMachine runAfter; |
| |
| // -------------------- Internal State -------------------- |
| @Nullable private RepositoryMapping platformRepositoryMapping; |
| @Nullable private PlatformInfo platformInfo; |
| @Nullable private NativeAndStarlarkFlags parsedFlags; |
| |
| PlatformFlagsProducer(Label platformLabel, ResultSink sink, StateMachine runAfter) { |
| this.platformLabel = platformLabel; |
| this.sink = sink; |
| this.runAfter = runAfter; |
| } |
| |
| @Override |
| public StateMachine step(Tasks tasks) throws InterruptedException { |
| // This needs a valid repository mapping in order to properly handle Starlark flags. |
| tasks.lookUp( |
| RepositoryMappingValue.key(this.platformLabel.getRepository()), |
| this::acceptRepositoryMappingValue); |
| // The platform info has the actual flags to process. |
| return new PlatformInfoProducer(this.platformLabel, this, this::parsePlatformFlags); |
| } |
| |
| private void acceptRepositoryMappingValue(SkyValue skyValue) { |
| if (skyValue instanceof RepositoryMappingValue) { |
| RepositoryMappingValue repositoryMappingValue = (RepositoryMappingValue) skyValue; |
| this.platformRepositoryMapping = repositoryMappingValue.getRepositoryMapping(); |
| return; |
| } |
| |
| throw new IllegalStateException("Unknown SkyValue type " + skyValue); |
| } |
| |
| @Override |
| public void acceptPlatformInfo(PlatformInfo info) { |
| this.platformInfo = info; |
| } |
| |
| @Override |
| public void acceptPlatformInfoError(InvalidPlatformException error) { |
| sink.acceptPlatformFlagsError(this.platformLabel, error); |
| } |
| |
| private StateMachine parsePlatformFlags(Tasks tasks) { |
| if (this.platformRepositoryMapping == null || this.platformInfo == null) { |
| return DONE; // There was an error. |
| } |
| |
| ImmutableList<String> flags = this.platformInfo.flags(); |
| if (flags.isEmpty()) { |
| // Nothing to do, so skip out now. |
| return this.runAfter; |
| } |
| |
| PackageContext packageContext = |
| PackageContext.of(platformLabel.getPackageIdentifier(), this.platformRepositoryMapping); |
| ParsedFlagsValue.Key parsedFlagsKey = ParsedFlagsValue.Key.create(flags, packageContext); |
| tasks.lookUp(parsedFlagsKey, OptionsParsingException.class, this::acceptParsedFlagsValue); |
| return this::handleParsedFlags; |
| } |
| |
| private void acceptParsedFlagsValue( |
| @Nullable SkyValue value, @Nullable OptionsParsingException exception) { |
| if (value != null && value instanceof ParsedFlagsValue) { |
| this.parsedFlags = ((ParsedFlagsValue) value).flags(); |
| return; |
| } |
| if (exception != null) { |
| sink.acceptPlatformFlagsError(this.platformLabel, exception); |
| return; |
| } |
| throw new IllegalStateException("Both value and exception are null"); |
| } |
| |
| private StateMachine handleParsedFlags(Tasks tasks) { |
| if (this.parsedFlags == null) { |
| return DONE; // There was an error. |
| } |
| sink.acceptPlatformFlags(this.platformLabel, this.parsedFlags); |
| return this.runAfter; |
| } |
| } |