Skip to content

Commit 20ecf30

Browse files
authored
feature(generator): add standalone executable with config file support (#6262)
* feature(generator): add standalone executable with config file support
1 parent dfd6de9 commit 20ecf30

File tree

6 files changed

+258
-6
lines changed

6 files changed

+258
-6
lines changed

generator/BUILD

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,30 @@ load(":google_cloud_cpp_generator_unit_tests.bzl", "google_cloud_cpp_generator_u
6060

6161
cc_binary(
6262
name = "protoc-gen-cpp_codegen",
63-
srcs = ["main.cc"],
64-
includes = ["."],
63+
srcs = ["plugin_main.cc"],
6564
visibility = ["//visibility:public"],
6665
deps = [":google_cloud_cpp_generator"],
6766
)
67+
68+
proto_library(
69+
name = "generator_config_proto",
70+
srcs = ["generator_config.proto"],
71+
)
72+
73+
cc_proto_library(
74+
name = "generator_config_cc_proto",
75+
deps = [":generator_config_proto"],
76+
)
77+
78+
cc_binary(
79+
name = "google-cloud-cpp-codegen",
80+
srcs = ["standalone_main.cc"],
81+
visibility = ["//visibility:public"],
82+
deps = [
83+
":generator_config_cc_proto",
84+
":google_cloud_cpp_generator",
85+
"@com_google_absl//absl/flags:commandlineflag",
86+
"@com_google_absl//absl/flags:flag",
87+
"@com_google_absl//absl/flags:parse",
88+
],
89+
)

generator/CMakeLists.txt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ set(DOXYGEN_EXAMPLE_PATH "")
1919
include(GoogleCloudCppCommon)
2020

2121
find_package(absl CONFIG REQUIRED)
22-
22+
find_package(Protobuf REQUIRED)
2323
find_package(ProtobufWithTargets REQUIRED)
2424

2525
add_library(
@@ -81,9 +81,29 @@ include(CreateBazelConfig)
8181
create_bazel_config(google_cloud_cpp_generator YEAR "2020")
8282

8383
# Build protoc plugin executable
84-
add_executable(protoc-gen-cpp_codegen main.cc)
85-
target_link_libraries(protoc-gen-cpp_codegen LINK_PUBLIC
86-
google_cloud_cpp_generator protobuf::libprotoc)
84+
add_executable(protoc-gen-cpp_codegen plugin_main.cc)
85+
target_link_libraries(
86+
protoc-gen-cpp_codegen LINK_PUBLIC google_cloud_cpp_generator
87+
protobuf::libprotoc ${Protobuf_LIBRARIES})
88+
89+
# Generate protobuf code and library
90+
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS generator_config.proto)
91+
add_library(google_cloud_cpp_generator_config ${PROTO_HDRS} ${PROTO_SRCS})
92+
target_link_libraries(google_cloud_cpp_generator_config
93+
PUBLIC protobuf::libprotobuf)
94+
set_target_properties(google_cloud_cpp_generator_config
95+
PROPERTIES CXX_CLANG_TIDY "")
96+
97+
# Build standalone executable
98+
add_executable(google-cloud-cpp-codegen standalone_main.cc)
99+
target_link_libraries(
100+
google-cloud-cpp-codegen
101+
LINK_PUBLIC
102+
google_cloud_cpp_generator_config
103+
google_cloud_cpp_generator
104+
protobuf::libprotoc
105+
absl::flags
106+
absl::flags_parse)
87107

88108
# Define the tests in a function so we have a new scope for variable names.
89109
function (google_cloud_cpp_generator_define_tests)

generator/generator_config.proto

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
package google.cloud.cpp.generator;
18+
19+
message ServiceConfiguration {
20+
string service_proto_path = 1;
21+
string product_path = 2;
22+
string initial_copyright_year = 3;
23+
repeated string omitted_rpcs = 4;
24+
}
25+
26+
message GeneratorConfiguration {
27+
repeated ServiceConfiguration service = 1;
28+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https:#www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# proto-file: generator/generator_config.proto
15+
# proto-message: GeneratorConfiguration
16+
17+
# BigQuery
18+
service {
19+
service_proto_path: "google/cloud/bigquery/storage/v1/storage.proto"
20+
product_path: "google/cloud/bigquery"
21+
initial_copyright_year: "2021"
22+
}
23+
24+
# IAM
25+
service {
26+
service_proto_path: "google/iam/credentials/v1/iamcredentials.proto"
27+
product_path: "google/cloud/iam"
28+
initial_copyright_year: "2020"
29+
}
30+
31+
# Logging
32+
service {
33+
service_proto_path: "google/logging/v2/logging.proto"
34+
product_path: "google/cloud/logging"
35+
initial_copyright_year: "2021"
36+
}

generator/standalone_main.cc

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "google/cloud/internal/absl_str_join_quiet.h"
16+
#include "google/cloud/log.h"
17+
#include "google/cloud/status_or.h"
18+
#include "absl/flags/flag.h"
19+
#include "absl/flags/parse.h"
20+
#include "generator/generator.h"
21+
#include "generator/generator_config.pb.h"
22+
#include <google/protobuf/compiler/command_line_interface.h>
23+
#include <google/protobuf/text_format.h>
24+
#include <fstream>
25+
#include <future>
26+
#include <iostream>
27+
#include <sstream>
28+
29+
ABSL_FLAG(std::string, config_file, "",
30+
"Text proto configuration file specifying the code to be generated.");
31+
ABSL_FLAG(std::string, googleapis_commit_hash, "",
32+
"Git commit hash of googleapis dependency.");
33+
ABSL_FLAG(std::string, protobuf_proto_path, "",
34+
"Path to root dir of protos distributed with protobuf.");
35+
ABSL_FLAG(std::string, googleapis_proto_path, "",
36+
"Path to root dir of protos distributed with googleapis.");
37+
ABSL_FLAG(std::string, output_path, ".",
38+
"Path to root dir where code is emitted.");
39+
40+
namespace {
41+
google::cloud::StatusOr<google::cloud::cpp::generator::GeneratorConfiguration>
42+
GetConfig(std::string const& filepath) {
43+
std::ifstream input(filepath);
44+
std::stringstream buffer;
45+
buffer << input.rdbuf();
46+
47+
google::cloud::cpp::generator::GeneratorConfiguration generator_config;
48+
auto parse_result = google::protobuf::TextFormat::ParseFromString(
49+
buffer.str(), &generator_config);
50+
51+
if (parse_result) return generator_config;
52+
return google::cloud::Status(google::cloud::StatusCode::kInvalidArgument,
53+
"Unable to parse textproto file.");
54+
}
55+
} // namespace
56+
57+
/**
58+
* C++ microgenerator.
59+
*
60+
* @par Command line arguments:
61+
* --config-file=<path> REQUIRED should be a textproto file for
62+
* GeneratorConfiguration message.
63+
* --googleapis_commit_hash=<hash> REQUIRED git commit hash of googleapis
64+
* dependency.
65+
* --protobuf_proto_path=<path> REQUIRED path to .proto files distributed with
66+
* protobuf.
67+
* --googleapis_proto_path=<path> REQUIRED path to .proto files distributed
68+
* with googleapis repo.
69+
* --output_path=<path> OPTIONAL defaults to current directory.
70+
*/
71+
int main(int argc, char** argv) {
72+
absl::ParseCommandLine(argc, argv);
73+
74+
auto proto_path = absl::GetFlag(FLAGS_protobuf_proto_path);
75+
auto googleapis_path = absl::GetFlag(FLAGS_googleapis_proto_path);
76+
auto googleapis_commit_hash = absl::GetFlag(FLAGS_googleapis_commit_hash);
77+
auto config_file = absl::GetFlag(FLAGS_config_file);
78+
auto output_path = absl::GetFlag(FLAGS_output_path);
79+
80+
GCP_LOG(INFO) << "proto_path = " << proto_path << "\n";
81+
GCP_LOG(INFO) << "googleapis_path = " << googleapis_path << "\n";
82+
GCP_LOG(INFO) << "googleapis_commit_hash = " << googleapis_commit_hash
83+
<< "\n";
84+
GCP_LOG(INFO) << "config_file = " << config_file << "\n";
85+
GCP_LOG(INFO) << "output_path = " << output_path << "\n";
86+
87+
auto config = GetConfig(config_file);
88+
if (!config.ok()) {
89+
GCP_LOG(ERROR) << "Failed to parse config file: " << config_file << "\n";
90+
}
91+
92+
std::vector<std::future<google::cloud::Status>> tasks;
93+
for (auto const& service : config->service()) {
94+
std::vector<std::string> args;
95+
// empty arg prevents first real arg from being ignored.
96+
args.emplace_back("");
97+
args.emplace_back("--proto_path=" + proto_path);
98+
args.emplace_back("--proto_path=" + googleapis_path);
99+
args.emplace_back("--cpp_codegen_out=" + output_path);
100+
args.emplace_back("--cpp_codegen_opt=product_path=" +
101+
service.product_path());
102+
args.emplace_back("--cpp_codegen_opt=googleapis_commit_hash=" +
103+
googleapis_commit_hash);
104+
args.emplace_back("--cpp_codegen_opt=copyright_year=" +
105+
service.initial_copyright_year());
106+
for (auto const& omit_rpc : service.omitted_rpcs()) {
107+
args.emplace_back("--cpp_codegen_opt=omit_rpc=" + omit_rpc);
108+
}
109+
args.emplace_back(service.service_proto_path());
110+
GCP_LOG(INFO) << "Generating service code using: "
111+
<< absl::StrJoin(args, ";") << "\n";
112+
113+
tasks.push_back(std::async(std::launch::async, [args] {
114+
google::protobuf::compiler::CommandLineInterface cli;
115+
google::cloud::generator::Generator generator;
116+
cli.RegisterGenerator("--cpp_codegen_out", "--cpp_codegen_opt",
117+
&generator, "Codegen C++ Generator");
118+
std::vector<char const*> c_args;
119+
c_args.reserve(args.size());
120+
for (auto const& arg : args) {
121+
c_args.push_back(arg.c_str());
122+
}
123+
124+
if (cli.Run(static_cast<int>(c_args.size()), c_args.data()) != 0)
125+
return google::cloud::Status(google::cloud::StatusCode::kInternal,
126+
absl::StrCat("Generating service from ",
127+
c_args.back(), " failed."));
128+
129+
return google::cloud::Status{};
130+
}));
131+
}
132+
133+
std::string error_message;
134+
for (auto& t : tasks) {
135+
auto result = t.get();
136+
if (!result.ok()) {
137+
absl::StrAppend(&error_message, result.message(), "\n");
138+
}
139+
}
140+
141+
if (!error_message.empty()) {
142+
GCP_LOG(ERROR) << error_message;
143+
return 1;
144+
}
145+
return 0;
146+
}

0 commit comments

Comments
 (0)