// Copyright 2014 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.query2.output; import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.ENVIRONMENT_GROUP; import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.GENERATED_FILE; import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.PACKAGE_GROUP; import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.RULE; import static com.google.devtools.build.lib.query2.proto.proto2api.Build.Target.Discriminator.SOURCE_FILE; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.graph.Digraph; import com.google.devtools.build.lib.packages.AggregatingAttributeMapper; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.AttributeSerializer; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.EnvironmentGroup; import com.google.devtools.build.lib.packages.InputFile; import com.google.devtools.build.lib.packages.OutputFile; import com.google.devtools.build.lib.packages.PackageGroup; import com.google.devtools.build.lib.packages.ProtoUtils; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.query2.FakeSubincludeTarget; import com.google.devtools.build.lib.query2.engine.OutputFormatterCallback; import com.google.devtools.build.lib.query2.output.AspectResolver.BuildFileDependencyMode; import com.google.devtools.build.lib.query2.output.OutputFormatter.AbstractUnorderedFormatter; import com.google.devtools.build.lib.query2.output.QueryOptions.OrderOutput; import com.google.devtools.build.lib.query2.proto.proto2api.Build; import com.google.devtools.build.lib.query2.proto.proto2api.Build.QueryResult.Builder; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.util.BinaryPredicate; import java.io.IOException; import java.io.PrintStream; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Map.Entry; import java.util.Set; /** * An output formatter that outputs a protocol buffer representation * of a query result and outputs the proto bytes to the output print stream. * By taking the bytes and calling {@code mergeFrom()} on a * {@code Build.QueryResult} object the full result can be reconstructed. */ public class ProtoOutputFormatter extends AbstractUnorderedFormatter { /** * A special attribute name for the rule implementation hash code. */ public static final String RULE_IMPLEMENTATION_HASH_ATTR_NAME = "$rule_implementation_hash"; private transient BinaryPredicate dependencyFilter; protected transient AspectResolver aspectResolver; private boolean relativeLocations = false; protected boolean includeDefaultValues = true; protected void setDependencyFilter(QueryOptions options) { this.dependencyFilter = OutputFormatter.getDependencyFilter(options); } @Override public String getName() { return "proto"; } @Override public OutputFormatterCallback createStreamCallback(QueryOptions options, final PrintStream out, AspectResolver aspectResolver) { relativeLocations = options.relativeLocations; this.aspectResolver = aspectResolver; this.includeDefaultValues = options.protoIncludeDefaultValues; setDependencyFilter(options); return new OutputFormatterCallback() { private Builder queryResult; @Override public void start() { queryResult = Build.QueryResult.newBuilder(); } @Override protected void processOutput(Iterable partialResult) throws IOException, InterruptedException { for (Target target : partialResult) { queryResult.addTarget(toTargetProtoBuffer(target)); } } @Override public void close() throws IOException { queryResult.build().writeTo(out); } }; } private static Iterable getSortedLabels(Digraph result) { return Iterables.transform( result.getTopologicalOrder(new TargetOrdering()), EXTRACT_NODE_LABEL); } @Override protected Iterable getOrderedTargets(Digraph result, QueryOptions options) { return options.orderOutput == OrderOutput.FULL ? getSortedLabels(result) : result.getLabels(); } /** * Converts a logical Target object into a Target protobuffer. */ protected Build.Target toTargetProtoBuffer(Target target) throws InterruptedException { Build.Target.Builder targetPb = Build.Target.newBuilder(); String location = getLocation(target, relativeLocations); if (target instanceof Rule) { Rule rule = (Rule) target; Build.Rule.Builder rulePb = Build.Rule.newBuilder() .setName(rule.getLabel().toString()) .setRuleClass(rule.getRuleClass()) .setLocation(location); for (Attribute attr : rule.getAttributes()) { if (!includeDefaultValues && !rule.isAttributeValueExplicitlySpecified(attr) || !includeAttribute(rule, attr)) { continue; } Iterable possibleAttributeValues = AggregatingAttributeMapper.of(rule).getPossibleAttributeValues(rule, attr); Object flattenedAttributeValue = AggregatingAttributeMapper.flattenAttributeValues( attr.getType(), possibleAttributeValues); Build.Attribute serializedAttribute = AttributeSerializer.getAttributeProto( attr, flattenedAttributeValue, rule.isAttributeValueExplicitlySpecified(attr), /*includeGlobs=*/ false, /*encodeBooleanAndTriStateAsIntegerAndString=*/ true); rulePb.addAttribute(serializedAttribute); } postProcess(rule, rulePb); Environment env = rule.getRuleClassObject().getRuleDefinitionEnvironment(); if (env != null) { // The RuleDefinitionEnvironment is always defined for Skylark rules and // always null for non Skylark rules. rulePb.addAttribute( Build.Attribute.newBuilder() .setName(RULE_IMPLEMENTATION_HASH_ATTR_NAME) .setType(ProtoUtils.getDiscriminatorFromType( com.google.devtools.build.lib.syntax.Type.STRING)) .setStringValue(env.getTransitiveContentHashCode())); } ImmutableMultimap aspectsDependencies = aspectResolver.computeAspectDependencies(target); // Add information about additional attributes from aspects. for (Entry> entry : aspectsDependencies.asMap().entrySet()) { Attribute attribute = entry.getKey(); Collection