1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc. All rights reserved.
3// https://developers.google.com/protocol-buffers/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// Author: kenton@google.com (Kenton Varda)
32
33#include <google/protobuf/compiler/plugin.h>
34
35#include <iostream>
36#include <set>
37
38#ifdef _WIN32
39#include <fcntl.h>
40#else
41#include <unistd.h>
42#endif
43
44#include <google/protobuf/stubs/logging.h>
45#include <google/protobuf/stubs/common.h>
46#include <google/protobuf/compiler/plugin.pb.h>
47#include <google/protobuf/compiler/code_generator.h>
48#include <google/protobuf/descriptor.h>
49#include <google/protobuf/descriptor.pb.h>
50#include <google/protobuf/io/io_win32.h>
51#include <google/protobuf/io/zero_copy_stream_impl.h>
52
53
54namespace google {
55namespace protobuf {
56namespace compiler {
57
58#if defined(_WIN32)
59// DO NOT include <io.h>, instead create functions in io_win32.{h,cc} and import
60// them like we do below.
61using google::protobuf::io::win32::setmode;
62#endif
63
64class GeneratorResponseContext : public GeneratorContext {
65 public:
66 GeneratorResponseContext(
67 const Version& compiler_version, CodeGeneratorResponse* response,
68 const std::vector<const FileDescriptor*>& parsed_files)
69 : compiler_version_(compiler_version),
70 response_(response),
71 parsed_files_(parsed_files) {}
72 ~GeneratorResponseContext() override {}
73
74 // implements GeneratorContext --------------------------------------
75
76 io::ZeroCopyOutputStream* Open(const std::string& filename) override {
77 CodeGeneratorResponse::File* file = response_->add_file();
78 file->set_name(filename);
79 return new io::StringOutputStream(file->mutable_content());
80 }
81
82 io::ZeroCopyOutputStream* OpenForInsert(
83 const std::string& filename,
84 const std::string& insertion_point) override {
85 CodeGeneratorResponse::File* file = response_->add_file();
86 file->set_name(filename);
87 file->set_insertion_point(insertion_point);
88 return new io::StringOutputStream(file->mutable_content());
89 }
90
91 io::ZeroCopyOutputStream* OpenForInsertWithGeneratedCodeInfo(
92 const std::string& filename, const std::string& insertion_point,
93 const google::protobuf::GeneratedCodeInfo& info) override {
94 CodeGeneratorResponse::File* file = response_->add_file();
95 file->set_name(filename);
96 file->set_insertion_point(insertion_point);
97 *file->mutable_generated_code_info() = info;
98 return new io::StringOutputStream(file->mutable_content());
99 }
100
101 void ListParsedFiles(std::vector<const FileDescriptor*>* output) override {
102 *output = parsed_files_;
103 }
104
105 void GetCompilerVersion(Version* version) const override {
106 *version = compiler_version_;
107 }
108
109 private:
110 Version compiler_version_;
111 CodeGeneratorResponse* response_;
112 const std::vector<const FileDescriptor*>& parsed_files_;
113};
114
115bool GenerateCode(const CodeGeneratorRequest& request,
116 const CodeGenerator& generator,
117 CodeGeneratorResponse* response, std::string* error_msg) {
118 DescriptorPool pool;
119 for (int i = 0; i < request.proto_file_size(); i++) {
120 const FileDescriptor* file = pool.BuildFile(proto: request.proto_file(index: i));
121 if (file == nullptr) {
122 // BuildFile() already wrote an error message.
123 return false;
124 }
125 }
126
127 std::vector<const FileDescriptor*> parsed_files;
128 for (int i = 0; i < request.file_to_generate_size(); i++) {
129 parsed_files.push_back(x: pool.FindFileByName(name: request.file_to_generate(index: i)));
130 if (parsed_files.back() == nullptr) {
131 *error_msg =
132 "protoc asked plugin to generate a file but "
133 "did not provide a descriptor for the file: " +
134 request.file_to_generate(index: i);
135 return false;
136 }
137 }
138
139 GeneratorResponseContext context(request.compiler_version(), response,
140 parsed_files);
141
142
143 std::string error;
144 bool succeeded = generator.GenerateAll(files: parsed_files, parameter: request.parameter(),
145 generator_context: &context, error: &error);
146
147 response->set_supported_features(generator.GetSupportedFeatures());
148
149 if (!succeeded && error.empty()) {
150 error =
151 "Code generator returned false but provided no error "
152 "description.";
153 }
154 if (!error.empty()) {
155 response->set_error(error);
156 }
157
158 return true;
159}
160
161int PluginMain(int argc, char* argv[], const CodeGenerator* generator) {
162
163 if (argc > 1) {
164 std::cerr << argv[0] << ": Unknown option: " << argv[1] << std::endl;
165 return 1;
166 }
167
168#ifdef _WIN32
169 setmode(STDIN_FILENO, _O_BINARY);
170 setmode(STDOUT_FILENO, _O_BINARY);
171#endif
172
173 CodeGeneratorRequest request;
174 if (!request.ParseFromFileDescriptor(STDIN_FILENO)) {
175 std::cerr << argv[0] << ": protoc sent unparseable request to plugin."
176 << std::endl;
177 return 1;
178 }
179
180
181 std::string error_msg;
182 CodeGeneratorResponse response;
183
184 if (GenerateCode(request, generator: *generator, response: &response, error_msg: &error_msg)) {
185 if (!response.SerializeToFileDescriptor(STDOUT_FILENO)) {
186 std::cerr << argv[0] << ": Error writing to stdout." << std::endl;
187 return 1;
188 }
189 } else {
190 if (!error_msg.empty()) {
191 std::cerr << argv[0] << ": " << error_msg << std::endl;
192 }
193 return 1;
194 }
195
196 return 0;
197}
198
199} // namespace compiler
200} // namespace protobuf
201} // namespace google
202