// 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.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.packages.ClassObjectConstructor; import com.google.devtools.build.lib.packages.NativeClassObjectConstructor; import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** Provider for a platform, which is a group of constraints and values. */ @SkylarkModule( name = "PlatformInfo", doc = "Provides access to data about a specific platform.", category = SkylarkModuleCategory.PROVIDER ) @Immutable public class PlatformInfo extends SkylarkClassObject { /** Name used in Skylark for accessing this provider. */ public static final String SKYLARK_NAME = "PlatformInfo"; /** Skylark constructor and identifier for this provider. */ public static final ClassObjectConstructor SKYLARK_CONSTRUCTOR = new NativeClassObjectConstructor(SKYLARK_NAME) {}; /** Identifier used to retrieve this provider from rules which export it. */ public static final SkylarkProviderIdentifier SKYLARK_IDENTIFIER = SkylarkProviderIdentifier.forKey(SKYLARK_CONSTRUCTOR.getKey()); private final ImmutableList<ConstraintValueInfo> constraints; private final ImmutableMap<String, String> remoteExecutionProperties; private PlatformInfo( ImmutableList<ConstraintValueInfo> constraints, ImmutableMap<String, String> remoteExecutionProperties) { super(SKYLARK_CONSTRUCTOR, ImmutableMap.<String, Object>of("constraints", constraints)); this.constraints = constraints; this.remoteExecutionProperties = remoteExecutionProperties; } public ImmutableList<ConstraintValueInfo> constraints() { return constraints; } @SkylarkCallable( name = "remoteExecutionProperties", doc = "Properties that are available for the use of remote execution.", structField = true ) public ImmutableMap<String, String> remoteExecutionProperties() { return remoteExecutionProperties; } /** 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 { private final List<ConstraintValueInfo> constraints = new ArrayList<>(); private final Map<String, String> remoteExecutionProperties = new HashMap<>(); /** * 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 */ public Builder addConstraint(ConstraintValueInfo constraint) { this.constraints.add(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 */ public Builder addConstraints(Iterable<ConstraintValueInfo> constraints) { for (ConstraintValueInfo constraint : constraints) { this.addConstraint(constraint); } return this; } /** * Adds the given key/value pair to the data being sent to a potential remote executor. If the * key already exists in the map, the previous value will be overwritten. * * @param key the key to be used * @param value the value to be used * @return the {@link Builder} instance for method chaining */ public Builder addRemoteExecutionProperty(String key, String value) { this.remoteExecutionProperties.put(key, value); return this; } /** * Adds the given properties to the data being sent to a potential remote executor. If any key * already exists in the map, the previous value will be overwritten. * * @param properties the properties to be added * @return the {@link Builder} instance for method chaining */ public Builder addRemoteExecutionProperties(Map<String, String> properties) { for (Map.Entry<String, String> entry : properties.entrySet()) { this.addRemoteExecutionProperty(entry.getKey(), entry.getValue()); } return this; } /** * 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 { ImmutableList<ConstraintValueInfo> validatedConstraints = validateConstraints(constraints); return new PlatformInfo(validatedConstraints, ImmutableMap.copyOf(remoteExecutionProperties)); } private ImmutableList<ConstraintValueInfo> validateConstraints( Iterable<ConstraintValueInfo> constraintValues) throws DuplicateConstraintException { Multimap<ConstraintSettingInfo, ConstraintValueInfo> constraints = ArrayListMultimap.create(); for (ConstraintValueInfo constraintValue : constraintValues) { constraints.put(constraintValue.constraint(), constraintValue); } // Are there any settings with more than one value? for (ConstraintSettingInfo constraintSetting : constraints.keySet()) { if (constraints.get(constraintSetting).size() > 1) { // Only reports the first case of this error. throw new DuplicateConstraintException( constraintSetting, constraints.get(constraintSetting)); } } return ImmutableList.copyOf(constraints.values()); } } /** * Exception class used when more than one {@link ConstraintValueInfo} for the same {@link * ConstraintSettingInfo} is added to a {@link Builder}. */ public static class DuplicateConstraintException extends Exception { private final ImmutableSet<ConstraintValueInfo> duplicateConstraints; public DuplicateConstraintException( ConstraintSettingInfo constraintSetting, Iterable<ConstraintValueInfo> duplicateConstraintValues) { super(formatError(constraintSetting, duplicateConstraintValues)); this.duplicateConstraints = ImmutableSet.copyOf(duplicateConstraintValues); } public ImmutableSet<ConstraintValueInfo> duplicateConstraints() { return duplicateConstraints; } private static String formatError( ConstraintSettingInfo constraintSetting, Iterable<ConstraintValueInfo> duplicateConstraints) { StringBuilder constraintValuesDescription = new StringBuilder(); for (ConstraintValueInfo constraintValue : duplicateConstraints) { if (constraintValuesDescription.length() > 0) { constraintValuesDescription.append(", "); } constraintValuesDescription.append(constraintValue.label()); } return String.format( "Duplicate constraint_values for constraint_setting %s: %s", constraintSetting.label(), constraintValuesDescription.toString()); } } }