• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 gRPC authors.
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 //     http://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 "grpc_tools/main.h"
16 
17 #include <algorithm>
18 #include <map>
19 #include <string>
20 #include <tuple>
21 #include <unordered_set>
22 #include <vector>
23 
24 #include <google/protobuf/compiler/code_generator.h>
25 #include <google/protobuf/compiler/command_line_interface.h>
26 #include <google/protobuf/compiler/importer.h>
27 #include <google/protobuf/compiler/python/generator.h>
28 #include <google/protobuf/compiler/python/pyi_generator.h>
29 #include <google/protobuf/descriptor.h>
30 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
31 
32 #include "absl/strings/string_view.h"
33 
34 #include "src/compiler/python_generator.h"
35 
36 using ::google::protobuf::FileDescriptor;
37 using ::google::protobuf::compiler::CodeGenerator;
38 using ::google::protobuf::compiler::DiskSourceTree;
39 using ::google::protobuf::compiler::GeneratorContext;
40 using ::google::protobuf::compiler::Importer;
41 using ::google::protobuf::compiler::MultiFileErrorCollector;
42 using ::google::protobuf::io::StringOutputStream;
43 using ::google::protobuf::io::ZeroCopyOutputStream;
44 
45 namespace grpc_tools {
protoc_main(int argc,char * argv[],char * version)46 int protoc_main(int argc, char* argv[], char* version) {
47   google::protobuf::compiler::CommandLineInterface cli;
48   cli.AllowPlugins("protoc-");
49 
50   // Proto2 Python
51   google::protobuf::compiler::python::Generator py_generator;
52   cli.RegisterGenerator("--python_out", &py_generator,
53                         "Generate Python source file.");
54 
55   // pyi files for type checking
56   google::protobuf::compiler::python::PyiGenerator pyi_generator;
57   cli.RegisterGenerator("--pyi_out", &pyi_generator,
58                         "Generate Python pyi stub.");
59 
60   // Get grpc_tools version
61   std::string grpc_tools_version = version;
62 
63   // gRPC Python
64   grpc_python_generator::GeneratorConfiguration grpc_py_config(
65       grpc_tools_version);
66   grpc_python_generator::PythonGrpcGenerator grpc_py_generator(grpc_py_config);
67   cli.RegisterGenerator("--grpc_python_out", &grpc_py_generator,
68                         "Generate Python source file.");
69 
70   return cli.Run(argc, argv);
71 }
72 
73 namespace internal {
74 
75 class GeneratorContextImpl : public GeneratorContext {
76  public:
GeneratorContextImpl(const std::vector<const FileDescriptor * > & parsed_files,std::vector<std::pair<std::string,std::string>> * files_out)77   GeneratorContextImpl(
78       const std::vector<const FileDescriptor*>& parsed_files,
79       std::vector<std::pair<std::string, std::string>>* files_out)
80       : files_(files_out), parsed_files_(parsed_files) {}
81 
Open(const std::string & filename)82   ZeroCopyOutputStream* Open(const std::string& filename) {
83     files_->emplace_back(filename, "");
84     return new StringOutputStream(&(files_->back().second));
85   }
86 
87   // NOTE(rbellevi): Equivalent to Open, since all files start out empty.
OpenForAppend(const std::string & filename)88   ZeroCopyOutputStream* OpenForAppend(const std::string& filename) {
89     return Open(filename);
90   }
91 
92   // NOTE(rbellevi): Equivalent to Open, since all files start out empty.
OpenForInsert(const std::string & filename,const std::string & insertion_point)93   ZeroCopyOutputStream* OpenForInsert(const std::string& filename,
94                                       const std::string& insertion_point) {
95     return Open(filename);
96   }
97 
ListParsedFiles(std::vector<const::google::protobuf::FileDescriptor * > * output)98   void ListParsedFiles(
99       std::vector<const ::google::protobuf::FileDescriptor*>* output) {
100     *output = parsed_files_;
101   }
102 
103  private:
104   std::vector<std::pair<std::string, std::string>>* files_;
105   const std::vector<const FileDescriptor*>& parsed_files_;
106 };
107 
108 class ErrorCollectorImpl : public MultiFileErrorCollector {
109  public:
ErrorCollectorImpl(std::vector<::grpc_tools::ProtocError> * errors,std::vector<::grpc_tools::ProtocWarning> * warnings)110   ErrorCollectorImpl(std::vector<::grpc_tools::ProtocError>* errors,
111                      std::vector<::grpc_tools::ProtocWarning>* warnings)
112       : errors_(errors), warnings_(warnings) {}
113 
RecordError(absl::string_view filename,int line,int column,absl::string_view message)114   void RecordError(absl::string_view filename, int line, int column,
115                    absl::string_view message) override {
116     errors_->emplace_back(std::string(filename), line, column,
117                           std::string(message));
118   }
119 
RecordWarning(absl::string_view filename,int line,int column,absl::string_view message)120   void RecordWarning(absl::string_view filename, int line, int column,
121                      absl::string_view message) override {
122     warnings_->emplace_back(std::string(filename), line, column,
123                             std::string(message));
124   }
125 
126  private:
127   std::vector<::grpc_tools::ProtocError>* errors_;
128   std::vector<::grpc_tools::ProtocWarning>* warnings_;
129 };
130 
calculate_transitive_closure(const FileDescriptor * descriptor,std::vector<const FileDescriptor * > * transitive_closure,std::unordered_set<const::google::protobuf::FileDescriptor * > * visited)131 static void calculate_transitive_closure(
132     const FileDescriptor* descriptor,
133     std::vector<const FileDescriptor*>* transitive_closure,
134     std::unordered_set<const ::google::protobuf::FileDescriptor*>* visited) {
135   for (int i = 0; i < descriptor->dependency_count(); ++i) {
136     const FileDescriptor* dependency = descriptor->dependency(i);
137     if (visited->find(dependency) == visited->end()) {
138       calculate_transitive_closure(dependency, transitive_closure, visited);
139     }
140   }
141   transitive_closure->push_back(descriptor);
142   visited->insert(descriptor);
143 }
144 
145 }  // end namespace internal
146 
generate_code(CodeGenerator * code_generator,char * protobuf_path,const std::vector<std::string> * include_paths,std::vector<std::pair<std::string,std::string>> * files_out,std::vector<::grpc_tools::ProtocError> * errors,std::vector<::grpc_tools::ProtocWarning> * warnings)147 static int generate_code(
148     CodeGenerator* code_generator, char* protobuf_path,
149     const std::vector<std::string>* include_paths,
150     std::vector<std::pair<std::string, std::string>>* files_out,
151     std::vector<::grpc_tools::ProtocError>* errors,
152     std::vector<::grpc_tools::ProtocWarning>* warnings) {
153   std::unique_ptr<internal::ErrorCollectorImpl> error_collector(
154       new internal::ErrorCollectorImpl(errors, warnings));
155   std::unique_ptr<DiskSourceTree> source_tree(new DiskSourceTree());
156   for (const auto& include_path : *include_paths) {
157     source_tree->MapPath("", include_path);
158   }
159   Importer importer(source_tree.get(), error_collector.get());
160   const FileDescriptor* parsed_file = importer.Import(protobuf_path);
161   if (parsed_file == nullptr) {
162     return 1;
163   }
164   std::vector<const FileDescriptor*> transitive_closure;
165   std::unordered_set<const FileDescriptor*> visited;
166   internal::calculate_transitive_closure(parsed_file, &transitive_closure,
167                                          &visited);
168   internal::GeneratorContextImpl generator_context(transitive_closure,
169                                                    files_out);
170   std::string error;
171   for (const auto descriptor : transitive_closure) {
172     code_generator->Generate(descriptor, "", &generator_context, &error);
173   }
174   return 0;
175 }
176 
protoc_get_protos(char * protobuf_path,const std::vector<std::string> * include_paths,std::vector<std::pair<std::string,std::string>> * files_out,std::vector<::grpc_tools::ProtocError> * errors,std::vector<::grpc_tools::ProtocWarning> * warnings)177 int protoc_get_protos(
178     char* protobuf_path, const std::vector<std::string>* include_paths,
179     std::vector<std::pair<std::string, std::string>>* files_out,
180     std::vector<::grpc_tools::ProtocError>* errors,
181     std::vector<::grpc_tools::ProtocWarning>* warnings) {
182   ::google::protobuf::compiler::python::Generator python_generator;
183   return generate_code(&python_generator, protobuf_path, include_paths,
184                        files_out, errors, warnings);
185 }
186 
protoc_get_services(char * protobuf_path,char * version,const std::vector<std::string> * include_paths,std::vector<std::pair<std::string,std::string>> * files_out,std::vector<::grpc_tools::ProtocError> * errors,std::vector<::grpc_tools::ProtocWarning> * warnings)187 int protoc_get_services(
188     char* protobuf_path, char* version,
189     const std::vector<std::string>* include_paths,
190     std::vector<std::pair<std::string, std::string>>* files_out,
191     std::vector<::grpc_tools::ProtocError>* errors,
192     std::vector<::grpc_tools::ProtocWarning>* warnings) {
193   std::string grpc_tools_version = version;
194   grpc_python_generator::GeneratorConfiguration grpc_py_config(
195       grpc_tools_version);
196   grpc_python_generator::PythonGrpcGenerator grpc_py_generator(grpc_py_config);
197   return generate_code(&grpc_py_generator, protobuf_path, include_paths,
198                        files_out, errors, warnings);
199 }
200 }  // end namespace grpc_tools
201