| // Copyright 2015 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.packages; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.devtools.build.lib.packages.Attribute.ComputedDefault; |
| import com.google.devtools.build.lib.query2.proto.proto2api.Build; |
| import javax.annotation.Nullable; |
| |
| /** Serialize a {@link Rule} as its protobuf representation. */ |
| public class RuleFormatter { |
| |
| // Skylark doesn't support defining rule classes with ComputedDefault attributes. Therefore, the |
| // only ComputedDefault attributes we expect to see for Skylark-defined rule classes are |
| // those declared in those rule classes' natively defined base rule classes, which are: |
| // |
| // 1. The "timeout" attribute in SkylarkRuleClassFunctions.testBaseRule |
| // 2. The "deprecation" attribute in BaseRuleClasses.commonCoreAndSkylarkAttributes |
| // 3. The "testonly" attribute in BaseRuleClasses.commonCoreAndSkylarkAttributes |
| private static final ImmutableSet<String> SKYLARK_RULE_CLASS_COMPUTED_DEFAULT_ATTRIBUTES = |
| ImmutableSet.of("timeout", "deprecation", "testonly"); |
| |
| public static Build.Rule.Builder serializeRule(Rule rule) { |
| Build.Rule.Builder builder = Build.Rule.newBuilder(); |
| builder.setName(rule.getLabel().getName()); |
| builder.setRuleClass(rule.getRuleClass()); |
| builder.setPublicByDefault(rule.getRuleClassObject().isPublicByDefault()); |
| |
| RawAttributeMapper rawAttributeMapper = RawAttributeMapper.of(rule); |
| boolean isSkylark = rule.getRuleClassObject().isSkylark(); |
| |
| if (isSkylark) { |
| builder.setSkylarkEnvironmentHashCode( |
| Preconditions.checkNotNull( |
| rule.getRuleClassObject().getRuleDefinitionEnvironmentHashCode(), rule)); |
| } |
| for (Attribute attr : rule.getAttributes()) { |
| Object rawAttributeValue = rawAttributeMapper.getRawAttributeValue(rule, attr); |
| boolean isExplicit = rule.isAttributeValueExplicitlySpecified(attr); |
| |
| if (!isSkylark && !isExplicit) { |
| // If the rule class is native (i.e. not Skylark-defined), then we can skip serialization |
| // of implicit attribute values. The native rule class can provide the same default value |
| // for the attribute after deserialization. |
| continue; |
| } |
| |
| Object valueToSerialize; |
| if (isExplicit) { |
| valueToSerialize = rawAttributeValue; |
| } else if (rawAttributeValue instanceof ComputedDefault) { |
| // If the rule class is Skylark-defined (i.e. rule.getRuleClassObject().isSkylark() is |
| // true), and the attribute has a ComputedDefault value, then we must serialize what it |
| // evaluates to. The Skylark-defined ComputedDefault function won't be available after |
| // deserialization due to Skylark's non-serializability. |
| valueToSerialize = evaluateSkylarkComputedDefault(rawAttributeMapper, attr); |
| if (valueToSerialize == null) { |
| continue; |
| } |
| } else { |
| // If the rule class is Skylark-defined and the attribute value is implicit, then we |
| // must serialize it. The Skylark-defined rule class won't be available after |
| // deserialization due to Skylark's non-serializability. |
| valueToSerialize = rawAttributeValue; |
| } |
| |
| builder.addAttribute( |
| AttributeFormatter.getAttributeProto( |
| attr, |
| valueToSerialize, |
| isExplicit, |
| /*encodeBooleanAndTriStateAsIntegerAndString=*/ false)); |
| } |
| return builder; |
| } |
| |
| /** |
| * Evaluates a {@link ComputedDefault} attribute value for a {@link Rule} with a Skylark-defined |
| * {@link RuleClass}. |
| * |
| * <p>We can't serialize ComputedDefault attributes defined in Skylark, because those can depend |
| * on other attributes which are configurable. |
| * |
| * <p>For a few attributes ({@link #SKYLARK_RULE_CLASS_COMPUTED_DEFAULT_ATTRIBUTES}), we know for |
| * certain that they don't have dependencies on other attributes which are configurable, so they |
| * can be evaluated here without loss of fidelity. |
| * |
| * <p>The {@link RawAttributeMapper#get} method, inherited from {@link AbstractAttributeMapper}, |
| * evaluates the {@link ComputedDefault} function, so we use that, after verifying the attribute's |
| * name is expected. |
| * |
| * @return the attribute's default value if we know it can be safely serialized, or null |
| * otherwise. |
| */ |
| @Nullable |
| private static Object evaluateSkylarkComputedDefault( |
| RawAttributeMapper rawAttributeMapper, Attribute attr) { |
| if (SKYLARK_RULE_CLASS_COMPUTED_DEFAULT_ATTRIBUTES.contains(attr.getName())) { |
| return rawAttributeMapper.get(attr.getName(), attr.getType()); |
| } |
| return null; |
| } |
| } |