| // 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.analysis.platform; |
| |
| import com.google.common.base.Strings; |
| import com.google.common.base.VerifyException; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.analysis.platform.ConstraintCollection.DuplicateConstraintException; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.packages.BuiltinProvider; |
| import com.google.devtools.build.lib.packages.NativeInfo; |
| import com.google.devtools.build.lib.starlarkbuildapi.platform.PlatformInfoApi; |
| import com.google.devtools.build.lib.util.Fingerprint; |
| import com.google.devtools.build.lib.util.StringUtilities; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import java.util.Objects; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.Printer; |
| import net.starlark.java.syntax.Location; |
| |
| /** Provider for a platform, which is a group of constraints and values. */ |
| @Immutable |
| public class PlatformInfo extends NativeInfo |
| implements PlatformInfoApi<ConstraintSettingInfo, ConstraintValueInfo> { |
| |
| /** |
| * The literal key that will be used to copy the {@link #remoteExecutionProperties} from the |
| * parent {@link PlatformInfo} into a new {@link PlatformInfo}'s {@link |
| * #remoteExecutionProperties}. |
| */ |
| public static final String PARENT_REMOTE_EXECUTION_KEY = "{PARENT_REMOTE_EXECUTION_PROPERTIES}"; |
| |
| /** Name used in Starlark for accessing this provider. */ |
| public static final String STARLARK_NAME = "PlatformInfo"; |
| |
| /** Empty {@link PlatformInfo} instance representing an invalid or empty platform. */ |
| public static final PlatformInfo EMPTY_PLATFORM_INFO; |
| |
| static { |
| try { |
| EMPTY_PLATFORM_INFO = PlatformInfo.builder().build(); |
| } catch (DuplicateConstraintException | ExecPropertiesException e) { |
| // This can never happen since we're not passing any values to the builder. |
| throw new VerifyException(e); |
| } |
| } |
| |
| /** Provider singleton constant. */ |
| public static final BuiltinProvider<PlatformInfo> PROVIDER = |
| new BuiltinProvider<PlatformInfo>(STARLARK_NAME, PlatformInfo.class) {}; |
| |
| private final Label label; |
| private final ConstraintCollection constraints; |
| private final String remoteExecutionProperties; |
| |
| /** execProperties will deprecate and replace remoteExecutionProperties */ |
| // TODO(blaze-configurability): If we want to remove remoteExecutionProperties, we need to fix |
| // PlatformUtils.getPlatformProto to use the dict-typed execProperties and do a migration. |
| private final PlatformProperties execProperties; |
| |
| private PlatformInfo( |
| Label label, |
| ConstraintCollection constraints, |
| String remoteExecutionProperties, |
| PlatformProperties execProperties, |
| Location creationLocation) { |
| super(creationLocation); |
| this.label = label; |
| this.constraints = constraints; |
| this.remoteExecutionProperties = Strings.nullToEmpty(remoteExecutionProperties); |
| this.execProperties = execProperties; |
| } |
| |
| @Override |
| public BuiltinProvider<PlatformInfo> getProvider() { |
| return PROVIDER; |
| } |
| |
| @Override |
| public Label label() { |
| return label; |
| } |
| |
| @Override |
| public ConstraintCollection constraints() { |
| return constraints; |
| } |
| |
| public String remoteExecutionProperties() { |
| return remoteExecutionProperties; |
| } |
| |
| public ImmutableMap<String, String> execProperties() { |
| return execProperties.properties(); |
| } |
| |
| @Override |
| public void repr(Printer printer) { |
| printer.append(String.format("PlatformInfo(%s, constraints=%s)", label, constraints)); |
| } |
| |
| /** Add this platform to the given fingerprint. */ |
| public void addTo(Fingerprint fp) { |
| fp.addString(label.toString()); |
| fp.addNullableString(remoteExecutionProperties); |
| fp.addStringMap(execProperties.properties()); |
| constraints.addToFingerprint(fp); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof PlatformInfo)) { |
| return false; |
| } |
| PlatformInfo that = (PlatformInfo) o; |
| return Objects.equals(label, that.label) |
| && Objects.equals(constraints, that.constraints) |
| && Objects.equals(remoteExecutionProperties, that.remoteExecutionProperties) |
| && Objects.equals(execProperties, that.execProperties); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(label, constraints, remoteExecutionProperties, execProperties); |
| } |
| |
| /** Returns a new {@link Builder} for creating a fresh {@link PlatformInfo} instance. */ |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| /** Builder class to facilitate creating valid {@link PlatformInfo} instances. */ |
| public static class Builder { |
| |
| @Nullable private PlatformInfo parent = null; |
| private Label label; |
| private final ConstraintCollection.Builder constraints = ConstraintCollection.builder(); |
| private String remoteExecutionProperties = null; |
| private final PlatformProperties.Builder execPropertiesBuilder = PlatformProperties.builder(); |
| private Location creationLocation = Location.BUILTIN; |
| |
| /** |
| * Sets the parent {@link PlatformInfo} that this platform inherits from. Constraint values set |
| * directly on this instance will be kept, but any other constraint settings will be found from |
| * the parent, if set. |
| * |
| * @param parent the platform that is the parent of this platform |
| * @return the {@link Builder} instance for method chaining |
| */ |
| @CanIgnoreReturnValue |
| public Builder setParent(@Nullable PlatformInfo parent) { |
| this.parent = parent; |
| if (parent == null) { |
| this.constraints.parent(null); |
| this.execPropertiesBuilder.setParent(null); |
| } else { |
| this.constraints.parent(parent.constraints); |
| this.execPropertiesBuilder.setParent(parent.execProperties); |
| } |
| return this; |
| } |
| |
| /** |
| * Sets the {@link Label} for this {@link PlatformInfo}. |
| * |
| * @param label the label identifying this platform |
| * @return the {@link Builder} instance for method chaining |
| */ |
| @CanIgnoreReturnValue |
| public Builder setLabel(Label label) { |
| this.label = label; |
| return this; |
| } |
| |
| /** |
| * Adds the given constraint value to the constraints that define this {@link PlatformInfo}. |
| * |
| * @param constraint the constraint to add |
| * @return the {@link Builder} instance for method chaining |
| */ |
| @CanIgnoreReturnValue |
| public Builder addConstraint(ConstraintValueInfo constraint) { |
| this.constraints.addConstraints(constraint); |
| return this; |
| } |
| |
| /** |
| * Adds the given constraint values to the constraints that define this {@link PlatformInfo}. |
| * |
| * @param constraints the constraints to add |
| * @return the {@link Builder} instance for method chaining |
| */ |
| @CanIgnoreReturnValue |
| public Builder addConstraints(Iterable<ConstraintValueInfo> constraints) { |
| this.constraints.addConstraints(constraints); |
| return this; |
| } |
| |
| /** |
| * Sets the data being sent to a potential remote executor. If there is a parent {@link |
| * PlatformInfo} set, the literal string "{PARENT_REMOTE_EXECUTION_PROPERTIES}" will be replaced |
| * by the {@link #remoteExecutionProperties} from that parent. Also if the parent is set, and |
| * this instance's {@link #remoteExecutionProperties} is blank or unset, the parent's will be |
| * used directly. |
| * |
| * <p>Specific examples: |
| * |
| * <ul> |
| * <li>parent.remoteExecutionProperties is unset: use the child's value |
| * <li>parent.remoteExecutionProperties is set, child.remoteExecutionProperties is unset: use |
| * the parent's value |
| * <li>parent.remoteExecutionProperties is set, child.remoteExecutionProperties is set, and |
| * does not contain {PARENT_REMOTE_EXECUTION_PROPERTIES}: use the child's value |
| * <li>parent.remoteExecutionProperties is set, child.remoteExecutionProperties is set, and |
| * does contain {PARENT_REMOTE_EXECUTION_PROPERTIES}: use the child's value, but |
| * substitute the parent's value for {PARENT_REMOTE_EXECUTION_PROPERTIES} |
| * </ul> |
| * |
| * @param properties the properties to be added |
| * @return the {@link Builder} instance for method chaining |
| */ |
| @CanIgnoreReturnValue |
| public Builder setRemoteExecutionProperties(String properties) { |
| this.remoteExecutionProperties = properties; |
| return this; |
| } |
| |
| /** |
| * Sets the execution properties. |
| * |
| * <p>If there is a parent {@link PlatformInfo} set, then all parent's properties will be |
| * inherited. Any properties included in both will use the child's value. Use the value of empty |
| * string to unset a property. |
| */ |
| @CanIgnoreReturnValue |
| public Builder setExecProperties(@Nullable ImmutableMap<String, String> properties) { |
| this.execPropertiesBuilder.setProperties(properties); |
| return this; |
| } |
| |
| /** |
| * Sets the {@link Location} where this {@link PlatformInfo} was created. |
| * |
| * @param location the location where the instance was created |
| * @return the {@link Builder} instance for method chaining |
| */ |
| @CanIgnoreReturnValue |
| public Builder setLocation(Location location) { |
| this.creationLocation = location; |
| return this; |
| } |
| |
| private static void checkRemoteExecutionProperties( |
| PlatformInfo parent, |
| String remoteExecutionProperties, |
| ImmutableMap<String, String> execProperties) |
| throws ExecPropertiesException { |
| if (!execProperties.isEmpty() && !Strings.isNullOrEmpty(remoteExecutionProperties)) { |
| throw new ExecPropertiesException( |
| "Platform contains both remote_execution_properties and exec_properties. Prefer" |
| + " exec_properties over the deprecated remote_execution_properties."); |
| } |
| if (!execProperties.isEmpty() |
| && parent != null |
| && !Strings.isNullOrEmpty(parent.remoteExecutionProperties())) { |
| throw new ExecPropertiesException( |
| "Platform specifies exec_properties but its parent " |
| + parent.label() |
| + " specifies remote_execution_properties. Prefer exec_properties over the" |
| + " deprecated remote_execution_properties."); |
| } |
| if (!Strings.isNullOrEmpty(remoteExecutionProperties) |
| && parent != null |
| && !parent.execProperties().isEmpty()) { |
| throw new ExecPropertiesException( |
| "Platform specifies remote_execution_properties but its parent specifies" |
| + " exec_properties. Prefer exec_properties over the deprecated" |
| + " remote_execution_properties."); |
| } |
| } |
| |
| /** |
| * Returns the new {@link PlatformInfo} instance. |
| * |
| * @throws DuplicateConstraintException if more than one constraint value exists for the same |
| * constraint setting |
| */ |
| public PlatformInfo build() throws DuplicateConstraintException, ExecPropertiesException { |
| checkRemoteExecutionProperties( |
| this.parent, this.remoteExecutionProperties, execPropertiesBuilder.getProperties()); |
| |
| // Merge the remote execution properties. |
| String remoteExecutionProperties = |
| mergeRemoteExecutionProperties(parent, this.remoteExecutionProperties); |
| |
| return new PlatformInfo( |
| label, |
| constraints.build(), |
| remoteExecutionProperties, |
| execPropertiesBuilder.build(), |
| creationLocation); |
| } |
| |
| private static String mergeRemoteExecutionProperties( |
| PlatformInfo parent, String remoteExecutionProperties) { |
| String parentRemoteExecutionProperties = ""; |
| if (parent != null) { |
| parentRemoteExecutionProperties = parent.remoteExecutionProperties(); |
| } |
| |
| if (remoteExecutionProperties == null) { |
| return parentRemoteExecutionProperties; |
| } |
| |
| return StringUtilities.replaceAllLiteral( |
| remoteExecutionProperties, PARENT_REMOTE_EXECUTION_KEY, parentRemoteExecutionProperties); |
| } |
| } |
| |
| /** Exception that indicates something is wrong in exec_properties configuration. */ |
| public static class ExecPropertiesException extends Exception { |
| ExecPropertiesException(String message) { |
| super(message); |
| } |
| } |
| } |