From e4da0992819b3f52f63adf28c25bcdfcfab6fd94 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 9 Apr 2024 12:45:50 +0200 Subject: [PATCH] Add `bazel help builtin-symbols-as-proto` RELNOTES: The new `bazel help builtin-symbols-as-proto` subcommand emits a base64-encoded proto message describing the built-in Starlark types and global symbols. See `src/main/protobuf/builtin.proto` for the message definition. --- .../com/google/devtools/build/lib/bazel/BUILD | 8 ++ .../lib/runtime/commands/HelpCommand.java | 28 +++++++ .../google/devtools/build/lib/runtime/BUILD | 18 +++++ .../lib/runtime/commands/HelpCommandTest.java | 77 +++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 src/test/java/com/google/devtools/build/lib/runtime/commands/HelpCommandTest.java diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/BUILD index 959505efd4d01e..b5b6181f5a4e21 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/BUILD +++ b/src/main/java/com/google/devtools/build/lib/bazel/BUILD @@ -213,6 +213,13 @@ java_library( ], ) +java_library( + name = "builtin_symbols_proto", + resources = [ + "//src/main/java/com/google/devtools/build/lib:gen_api_proto", + ], +) + java_binary( name = "BazelServer", javacopts = [ @@ -220,6 +227,7 @@ java_binary( ], main_class = "com.google.devtools.build.lib.bazel.Bazel", runtime_deps = [ + ":builtin_symbols_proto", # Adding this to :main would create a cycle ":main", "//src/main/java/com/google/devtools/build/lib/server", "//src/main/java/com/google/devtools/build/lib/util:simple_log_handler", # See startup_options.cc diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java index b4a0aedc0c6dd0..f98194477d42af 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/HelpCommand.java @@ -27,6 +27,7 @@ import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.NoBuildEvent; import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.runtime.BlazeCommand; import com.google.devtools.build.lib.runtime.BlazeCommandResult; import com.google.devtools.build.lib.runtime.BlazeCommandUtils; @@ -52,6 +53,8 @@ import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.OptionsParser.HelpVerbosity; import com.google.devtools.common.options.OptionsParsingResult; +import java.io.BufferedInputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -155,6 +158,8 @@ public BlazeCommandResult exec(CommandEnvironment env, OptionsParsingResult opti case "flags-as-proto": emitFlagsAsProtoHelp(runtime, outErr); return BlazeCommandResult.success(); + case "builtin-symbols-as-proto": + return emitBuiltinSymbolsAsProtoHelp(env.getReporter(), outErr); case "everything-as-html": new HtmlEmitter(runtime).emit(outErr); return BlazeCommandResult.success(); @@ -263,6 +268,29 @@ private static void emitFlagsAsProtoHelp(BlazeRuntime runtime, OutErr outErr) { outErr.printOut(Base64.getEncoder().encodeToString(collectionBuilder.build().toByteArray())); } + private BlazeCommandResult emitBuiltinSymbolsAsProtoHelp(Reporter reporter, OutErr outErr) { + try { + byte[] builtinsProtoBytes; + try (var in = + HelpCommand.class + .getClassLoader() + .getResourceAsStream("com/google/devtools/build/lib/builtin.pb")) { + if (in == null) { + throw new IOException("Could not find built-in symbols proto file"); + } + try (var bufferedIn = new BufferedInputStream(in)) { + builtinsProtoBytes = bufferedIn.readAllBytes(); + } + } + outErr.printOut(Base64.getEncoder().encodeToString(builtinsProtoBytes)); + } catch (IOException e) { + String message = "Failed to get built-in symbols proto: " + e.getMessage(); + reporter.handle(Event.error(null, message)); + return createFailureResult(message, Code.COMMAND_NOT_FOUND); + } + return BlazeCommandResult.success(); + } + private static BazelFlagsProto.FlagInfo.Builder createFlagInfo(OptionDefinition option) { BazelFlagsProto.FlagInfo.Builder flagBuilder = BazelFlagsProto.FlagInfo.newBuilder(); flagBuilder.setName(option.getOptionName()); diff --git a/src/test/java/com/google/devtools/build/lib/runtime/BUILD b/src/test/java/com/google/devtools/build/lib/runtime/BUILD index ac47c915a8030b..139a0e50854ee5 100644 --- a/src/test/java/com/google/devtools/build/lib/runtime/BUILD +++ b/src/test/java/com/google/devtools/build/lib/runtime/BUILD @@ -16,6 +16,7 @@ java_library( exclude = [ "commands/ConfigCommandTest.java", "commands/DumpCommandTest.java", + "commands/HelpCommandTest.java", ], ), deps = [ @@ -161,6 +162,23 @@ java_test( ], ) +java_test( + name = "HelpCommandTest", + srcs = ["commands/HelpCommandTest.java"], + deps = [ + "//src/main/java/com/google/devtools/build/lib:runtime", + "//src/main/java/com/google/devtools/build/lib:runtime/blaze_command_result", + "//src/main/java/com/google/devtools/build/lib/bazel:builtin_symbols_proto", + "//src/main/java/com/google/devtools/build/lib/runtime/commands", + "//src/main/java/com/google/devtools/build/lib/util/io:out-err", + "//src/main/protobuf:builtin_java_proto", + "//src/test/java/com/google/devtools/build/lib/buildtool/util", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + java_test( name = "RuntimeTests", test_class = "com.google.devtools.build.lib.AllTests", diff --git a/src/test/java/com/google/devtools/build/lib/runtime/commands/HelpCommandTest.java b/src/test/java/com/google/devtools/build/lib/runtime/commands/HelpCommandTest.java new file mode 100644 index 00000000000000..df52e3faac0df9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/runtime/commands/HelpCommandTest.java @@ -0,0 +1,77 @@ +// Copyright 2022 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.commands; + +import static com.google.common.truth.Truth.assertThat; +import static java.util.stream.Collectors.toMap; + +import com.google.common.collect.Lists; +import com.google.devtools.build.docgen.builtin.BuiltinProtos; +import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase; +import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher; +import com.google.devtools.build.lib.runtime.BlazeCommandResult; +import com.google.devtools.build.lib.runtime.BlazeRuntime; +import com.google.devtools.build.lib.util.io.RecordingOutErr; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link HelpCommand}. */ +@RunWith(JUnit4.class) +public final class HelpCommandTest extends BuildIntegrationTestCase { + private BlazeCommandDispatcher dispatcher; + private RecordingOutErr recordingOutErr; + + @Before + public void createDispatcher() { + BlazeRuntime runtime = getRuntime(); + runtime.getCommandMap().put("help", new HelpCommand()); + dispatcher = new BlazeCommandDispatcher(runtime); + } + + @Before + public void createRecording() { + recordingOutErr = new RecordingOutErr(); + } + + private BlazeCommandResult help(String... args) throws InterruptedException { + List params = Lists.newArrayList("help"); + Collections.addAll(params, args); + return dispatcher.exec(params, "test", recordingOutErr); + } + + @Test + public void wellKnownCallablesInBuiltinSymbolsProto() throws Exception { + assertThat(help("builtin-symbols-as-proto").isSuccess()).isTrue(); + assertThat(recordingOutErr.errAsLatin1()).isEmpty(); + String base64Proto = recordingOutErr.outAsLatin1(); + byte[] rawProto = Base64.getDecoder().decode(base64Proto); + var builtins = BuiltinProtos.Builtins.parseFrom(rawProto); + + assertThat( + builtins.getGlobalList().stream() + .filter(BuiltinProtos.Value::hasCallable) + .filter(global -> !global.getCallable().getParamList().isEmpty()) + .collect(toMap(BuiltinProtos.Value::getName, BuiltinProtos.Value::getApiContext))) + .containsAtLeast( + "range", BuiltinProtos.ApiContext.ALL, + "glob", BuiltinProtos.ApiContext.BUILD, + "DefaultInfo", BuiltinProtos.ApiContext.BZL); + } +}