// Copyright 2018 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; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.devtools.build.lib.analysis.AnalysisProtos; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.AttributeFormatter; import com.google.devtools.build.lib.packages.ConfiguredAttributeMapper; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; import com.google.devtools.build.lib.query2.output.AspectResolver; import com.google.devtools.build.lib.query2.output.CqueryOptions; import com.google.devtools.build.lib.query2.output.ProtoOutputFormatter; import com.google.devtools.build.lib.query2.proto.proto2api.Build; import com.google.devtools.build.lib.query2.proto.proto2api.Build.QueryResult; import com.google.devtools.build.lib.rules.AliasConfiguredTarget; import com.google.devtools.build.lib.skyframe.SkyframeExecutor; import java.io.IOException; import java.io.OutputStream; import java.util.Map; /** Proto output formatter for cquery results. */ public class ProtoOutputFormatterCallback extends CqueryThreadsafeCallback { private final AspectResolver resolver; private AnalysisProtos.CqueryResult.Builder protoResult; private ConfiguredTarget currentTarget; ProtoOutputFormatterCallback( Reporter reporter, CqueryOptions options, OutputStream out, SkyframeExecutor skyframeExecutor, TargetAccessor accessor, AspectResolver resolver) { super(reporter, options, out, skyframeExecutor, accessor); this.resolver = resolver; } @Override public void start() { protoResult = AnalysisProtos.CqueryResult.newBuilder(); } @Override public void close(boolean failFast) throws IOException { if (!failFast && printStream != null) { if (options.protoIncludeConfigurations) { protoResult.build().writeTo(printStream); } else { // Documentation promises that setting this flag to false means we convert directly // to the build.proto format. This is hard to test in integration testing due to the way // proto output is turned readable (codex). So change the following code with caution. QueryResult.Builder queryResult = Build.QueryResult.newBuilder(); protoResult.getResultsList().forEach(ct -> queryResult.addTarget(ct.getTarget())); queryResult.build().writeTo(printStream); } } } @Override public String getName() { return "proto"; } @VisibleForTesting public AnalysisProtos.CqueryResult getProtoResult() { return protoResult.build(); } @Override public void processOutput(Iterable partialResult) throws InterruptedException { ConfiguredProtoOutputFormatter formatter = new ConfiguredProtoOutputFormatter(); formatter.setOptions(options, resolver); for (ConfiguredTarget configuredTarget : partialResult) { AnalysisProtos.ConfiguredTarget.Builder builder = AnalysisProtos.ConfiguredTarget.newBuilder(); // Re: testing. Since this formatter relies on the heavily tested ProtoOutputFormatter class // for all its work with targets, ProtoOuputFormatterCallbackTest doesn't test any of the // logic in this next line. If this were to change (i.e. we manipulate targets any further), // we will want to add relevant tests. currentTarget = configuredTarget; builder.setTarget( formatter.toTargetProtoBuffer(accessor.getTargetFromConfiguredTarget(configuredTarget))); if (options.protoIncludeConfigurations) { String checksum = configuredTarget.getConfigurationChecksum(); builder.setConfiguration( AnalysisProtos.Configuration.newBuilder().setChecksum(String.valueOf(checksum))); } protoResult.addResults(builder.build()); } } private class ConfiguredProtoOutputFormatter extends ProtoOutputFormatter { @Override protected void addAttributes(Build.Rule.Builder rulePb, Rule rule) throws InterruptedException { ConfiguredTarget ctForConfigConditions = currentTarget; while (ctForConfigConditions instanceof AliasConfiguredTarget) { ctForConfigConditions = ((AliasConfiguredTarget) ctForConfigConditions).getActual(); } ImmutableMap configConditions = ((RuleConfiguredTarget) ctForConfigConditions).getConfigConditions(); ConfiguredAttributeMapper attributeMapper = ConfiguredAttributeMapper.of(rule, configConditions); Map serializedAttributes = Maps.newHashMap(); for (Attribute attr : rule.getAttributes()) { if (!shouldIncludeAttribute(rule, attr)) { continue; } Object attributeValue = attributeMapper.get(attr.getName(), attr.getType()); Build.Attribute serializedAttribute = AttributeFormatter.getAttributeProto( attr, attributeValue, rule.isAttributeValueExplicitlySpecified(attr), /*encodeBooleanAndTriStateAsIntegerAndString=*/ true); serializedAttributes.put(attr, serializedAttribute); } rulePb.addAllAttribute(serializedAttributes.values()); postProcess(rule, rulePb, serializedAttributes); } } }