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