// 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.runtime; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import com.google.devtools.build.lib.buildeventstream.BuildEventContext; import com.google.devtools.build.lib.buildeventstream.BuildEventId; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEvent; import com.google.devtools.build.lib.buildeventstream.BuildEventWithOrderConstraint; import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent; import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.ChunkList; import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.CommandLine; import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.CommandLineSection; import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.Option; import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.OptionList; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.common.options.OptionDefinition; import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionMetadataTag; import com.google.devtools.common.options.OptionPriority; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.OptionsParsingException; import com.google.devtools.common.options.OptionsProvider; import com.google.devtools.common.options.ParsedOptionDescription; import com.google.devtools.common.options.proto.OptionFilters; import com.google.protobuf.InvalidProtocolBufferException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import javax.annotation.Nullable; /** A build event reporting the command line by which Bazel was invoked. */ public abstract class CommandLineEvent implements BuildEventWithOrderConstraint { @Override public Collection getChildrenEvents() { return ImmutableList.of(); } @Override public Collection postedAfter() { return ImmutableList.of(BuildEventId.buildStartedId()); } /** A CommandLineEvent that stores functions and values common to both Bazel command lines. */ public abstract static class BazelCommandLineEvent extends CommandLineEvent { protected final String productName; protected final OptionsProvider activeStartupOptions; protected final String commandName; protected final OptionsProvider commandOptions; BazelCommandLineEvent( String productName, OptionsProvider activeStartupOptions, String commandName, OptionsProvider commandOptions) { this.productName = productName; this.activeStartupOptions = activeStartupOptions; this.commandName = commandName; this.commandOptions = commandOptions; } CommandLineSection getExecutableSection() { return CommandLineSection.newBuilder() .setSectionLabel("executable") .setChunkList(ChunkList.newBuilder().addChunk(productName)) .build(); } CommandLineSection getCommandSection() { return CommandLineSection.newBuilder() .setSectionLabel("command") .setChunkList(ChunkList.newBuilder().addChunk(commandName)) .build(); } /** * Convert an array of tags to the equivalent proto-generated enum values. * *

The proto type is duplicate in order to not burden the OptionsParser with the proto * dependency. A test guarantees that the two enum types are kept in sync with matching indices. */ static List getProtoEffectTags(OptionEffectTag[] tagArray) { ArrayList effectTags = new ArrayList<>(tagArray.length); for (OptionEffectTag tag : tagArray) { effectTags.add(OptionFilters.OptionEffectTag.forNumber(tag.getValue())); } return effectTags; } /** * Convert an array of tags to the equivalent proto-generated enum values. * *

The proto type is duplicate in order to not burden the OptionsParser with the proto * dependency. A test guarantees that the two enum types are kept in sync with matching indices. */ static List getProtoMetadataTags( OptionMetadataTag[] tagArray) { ArrayList metadataTags = new ArrayList<>(tagArray.length); for (OptionMetadataTag tag : tagArray) { metadataTags.add(OptionFilters.OptionMetadataTag.forNumber(tag.getValue())); } return metadataTags; } List

Since in this command line the command options include invocation policy's and rcs' * contents expanded fully, the list of startup options should prevent reapplication of these * contents. * *

The options parser does not understand the effect of these flags, since the relationship * between these startup options and the command options is not held within the options parser, * so instead, we add a small hack. Remove any explicit mentions of these flags, and explicitly * add the options that prevent Blaze from looking for the default rc files. */ private CommandLineSection getCanonicalStartupOptions() { List

Permits Bazel to report command lines from the tool that invoked it, if such a tool exists. */ public static final class ToolCommandLineEvent extends CommandLineEvent { public static final String LABEL = "tool"; private final CommandLine commandLine; ToolCommandLineEvent(CommandLine commandLine) { this.commandLine = commandLine; } @Override public BuildEvent asStreamProto(BuildEventContext converters) { return GenericBuildEvent.protoChaining(this).setStructuredCommandLine(commandLine).build(); } /** * The label of this command line event is always "tool," so that the BuildStartingEvent * correctly tracks its children. The provided command line may have its own label that will be * more descriptive. */ @Override public BuildEventId getEventId() { return BuildEventId.structuredCommandlineId(LABEL); } /** * The converter for the option value. We accept the command line both in base64 encoded proto * form and as unstructured strings. */ public static class Converter implements com.google.devtools.common.options.Converter { @Override public ToolCommandLineEvent convert(String input) throws OptionsParsingException { if (input.isEmpty()) { return new ToolCommandLineEvent(CommandLine.getDefaultInstance()); } CommandLine commandLine; try { // Try decoding the input as a base64 encoded binary proto. commandLine = CommandLine.parseFrom(BaseEncoding.base64().decode(input)); } catch (IllegalArgumentException e) { // If the value was not recognized as a base64-encoded proto, store the flag value as a // single string chunk. commandLine = CommandLine.newBuilder() .setCommandLineLabel(LABEL) .addSections( CommandLineSection.newBuilder() .setChunkList(ChunkList.newBuilder().addChunk(input))) .build(); } catch (InvalidProtocolBufferException e) { throw new OptionsParsingException( String.format("Malformed value of --experimental_tool_command_line: %s", input), e); } return new ToolCommandLineEvent(commandLine); } @Override public String getTypeDescription() { return "A command line, either as a simple string, or as a base64-encoded binary form of a" + " CommandLine proto"; } } } }