|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +# Copyright 2023 Google LLC |
| 4 | +# |
| 5 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | +# you may not use this file except in compliance with the License. |
| 7 | +# You may obtain a copy of the License at |
| 8 | +# |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# Unless required by applicable law or agreed to in writing, software |
| 12 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +# See the License for the specific language governing permissions and |
| 15 | +# limitations under the License. |
| 16 | + |
| 17 | +desc "Run the generator manually" |
| 18 | + |
| 19 | +long_desc \ |
| 20 | + "Run the generator manually", |
| 21 | + "", |
| 22 | + "This tool can be used to do ad hoc generator runs for testing purposes. " \ |
| 23 | + "You much provide the input proto files (as individual files or " \ |
| 24 | + "directories). Those inputs must be within the googleapis directory " \ |
| 25 | + "structure. (You can use the googleapis submodule or provide your own " \ |
| 26 | + "clone of googleapis.) The tool also requires a BUILD.bazel file; you " \ |
| 27 | + "can provide the path to it directly or have the tool search for one " \ |
| 28 | + "colocated with the input protos. The tool will then generate the gem " \ |
| 29 | + "output files in the current directory or a configured output directory.", |
| 30 | + "", |
| 31 | + "Note that the tool reads some settings from the BUILD.bazel file, but " \ |
| 32 | + "cannot determine the protos to compile from the bazel settings, so you " \ |
| 33 | + "must explicitly include all such proto paths on the command line.", |
| 34 | + "", |
| 35 | + "Example:", |
| 36 | + [" toys run google/cloud/secretmanager/v1 --output=tmp"] |
| 37 | + |
| 38 | +include :bundler |
| 39 | +include :exec, e: true |
| 40 | +include :fileutils |
| 41 | + |
| 42 | +remaining_args :inputs do |
| 43 | + desc "Paths to input protos or directories that will be searched for protos" |
| 44 | +end |
| 45 | +flag :bazel_file, "--bazel-file=PATH" do |
| 46 | + desc "Path to the bazel file." |
| 47 | + long_desc \ |
| 48 | + "Path to the bazel file. If omitted, uses the first BUILD.bazel file " \ |
| 49 | + "found either at the top level of an input directory or as a sibling " \ |
| 50 | + "of an input proto file." |
| 51 | +end |
| 52 | +flag :proto_dir, "--proto-dir=PATH" do |
| 53 | + desc "Path to the protos dir. Defaults to shared/googleapis in this repo." |
| 54 | +end |
| 55 | +flag :output_dir, "--output-dir=PATH" do |
| 56 | + desc "Path to the directory to write output files. Defaults to the current directory." |
| 57 | +end |
| 58 | + |
| 59 | +def run |
| 60 | + setup |
| 61 | + load_bazel_params |
| 62 | + cmd = [ |
| 63 | + "grpc_tools_ruby_protoc", |
| 64 | + "--ruby_out", output_dir, |
| 65 | + "--grpc_out", output_dir, |
| 66 | + "--ruby_cloud_out", output_dir, |
| 67 | + "--proto_path", proto_dir, |
| 68 | + "--ruby_cloud_opt", @protoc_params.join(","), |
| 69 | + ] + inputs |
| 70 | + exec cmd |
| 71 | +end |
| 72 | + |
| 73 | +def setup |
| 74 | + set :proto_dir, File.join(context_directory, "shared", "googleapis") unless proto_dir |
| 75 | + set :output_dir, File.expand_path(output_dir, Dir.getwd) |
| 76 | + mkdir_p output_dir |
| 77 | + search_inputs |
| 78 | + unless bazel_file |
| 79 | + puts "Did not find BUILD.bazel" |
| 80 | + exit 1 |
| 81 | + end |
| 82 | + if inputs.empty? |
| 83 | + puts "Did not find any input proto files" |
| 84 | + exit 1 |
| 85 | + end |
| 86 | +end |
| 87 | + |
| 88 | +def search_inputs |
| 89 | + found_inputs = [] |
| 90 | + found_bazel_file = nil |
| 91 | + inputs.each do |input| |
| 92 | + input = File.expand_path(input, proto_dir) |
| 93 | + if File.directory? input |
| 94 | + found_inputs += Dir.glob("#{input}/**/*.proto") |
| 95 | + elsif File.file? input |
| 96 | + found_inputs << input |
| 97 | + input = File.dirname input |
| 98 | + end |
| 99 | + next if found_bazel_file |
| 100 | + file = File.join input, "BUILD.bazel" |
| 101 | + found_bazel_file = file if File.file? file |
| 102 | + end |
| 103 | + set :inputs, found_inputs |
| 104 | + set :bazel_file, found_bazel_file |
| 105 | +end |
| 106 | + |
| 107 | +def load_bazel_params |
| 108 | + content = File.read bazel_file |
| 109 | + unless /ruby_cloud_gapic_library\(\n((?: .*\n)+)\)\n/ =~ content |
| 110 | + logger.error "Unable to find ruby_cloud_gapic_library in #{bazel_file}" |
| 111 | + exit 1 |
| 112 | + end |
| 113 | + content = Regexp.last_match[1] |
| 114 | + unless /extra_protoc_parameters = \[([^\]]+)\],/m =~ content |
| 115 | + logger.error "Unable to find ruby extra_protoc_parameters in #{bazel_file}" |
| 116 | + exit 1 |
| 117 | + end |
| 118 | + @protoc_params = Regexp.last_match[1].scan(/"([^"]+)"/) |
| 119 | + interpret_bazel_field(content, "grpc_service_config") { |str| interpret_path str } |
| 120 | + interpret_bazel_field(content, "service_yaml") { |str| interpret_path str } |
| 121 | + interpret_bazel_field content, "ruby_cloud_description" |
| 122 | + interpret_bazel_field content, "ruby_cloud_title" |
| 123 | + interpret_bazel_field(content, "transport", "transports") { |str| str.split("+").join ";" } |
| 124 | +end |
| 125 | + |
| 126 | +def interpret_bazel_field content, name, flag = nil |
| 127 | + if /#{name}\s*=\s*"([^"]+)"/ =~ content |
| 128 | + str = Regexp.last_match[1] |
| 129 | + str = yield str if block_given? |
| 130 | + str = str.gsub(",", "\\,").gsub("=", "\\=") |
| 131 | + flag ||= name.tr "_", "-" |
| 132 | + @protoc_params.append("#{flag}=#{str}") |
| 133 | + end |
| 134 | +end |
| 135 | + |
| 136 | +def interpret_path str |
| 137 | + if str.start_with? "//" |
| 138 | + File.join proto_dir, str.sub(%r{^//}, "").tr(":", "/") |
| 139 | + else |
| 140 | + File.join File.dirname(bazel_file), str.sub(/^:/, "") |
| 141 | + end |
| 142 | +end |
0 commit comments