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 | // Based on original Protocol Buffers design by |
33 | // Sanjay Ghemawat, Jeff Dean, and others. |
34 | |
35 | #include <google/protobuf/compiler/cpp/generator.h> |
36 | |
37 | #include <memory> |
38 | #include <string> |
39 | #include <utility> |
40 | #include <vector> |
41 | |
42 | #include <google/protobuf/stubs/strutil.h> |
43 | #include <google/protobuf/io/printer.h> |
44 | #include <google/protobuf/io/zero_copy_stream.h> |
45 | #include <google/protobuf/compiler/cpp/file.h> |
46 | #include <google/protobuf/compiler/cpp/helpers.h> |
47 | #include <google/protobuf/descriptor.pb.h> |
48 | |
49 | namespace google { |
50 | namespace protobuf { |
51 | namespace compiler { |
52 | namespace cpp { |
53 | |
54 | CppGenerator::CppGenerator() {} |
55 | CppGenerator::~CppGenerator() {} |
56 | |
57 | namespace { |
58 | std::string NumberedCcFileName(const std::string& basename, int number) { |
59 | return StrCat(a: basename, b: ".out/" , c: number, d: ".cc" ); |
60 | } |
61 | } // namespace |
62 | |
63 | bool CppGenerator::Generate(const FileDescriptor* file, |
64 | const std::string& parameter, |
65 | GeneratorContext* generator_context, |
66 | std::string* error) const { |
67 | std::vector<std::pair<std::string, std::string> > options; |
68 | ParseGeneratorParameter(parameter, &options); |
69 | |
70 | // ----------------------------------------------------------------- |
71 | // parse generator options |
72 | |
73 | // If the dllexport_decl option is passed to the compiler, we need to write |
74 | // it in front of every symbol that should be exported if this .proto is |
75 | // compiled into a Windows DLL. E.g., if the user invokes the protocol |
76 | // compiler as: |
77 | // protoc --cpp_out=dllexport_decl=FOO_EXPORT:outdir foo.proto |
78 | // then we'll define classes like this: |
79 | // class FOO_EXPORT Foo { |
80 | // ... |
81 | // } |
82 | // FOO_EXPORT is a macro which should expand to __declspec(dllexport) or |
83 | // __declspec(dllimport) depending on what is being compiled. |
84 | // |
85 | // If the proto_h option is passed to the compiler, we will generate all |
86 | // classes and enums so that they can be forward-declared from files that |
87 | // need them from imports. |
88 | // |
89 | // If the lite option is passed to the compiler, we will generate the |
90 | // current files and all transitive dependencies using the LITE runtime. |
91 | Options file_options; |
92 | |
93 | file_options.opensource_runtime = opensource_runtime_; |
94 | file_options.runtime_include_base = runtime_include_base_; |
95 | |
96 | for (int i = 0; i < options.size(); i++) { |
97 | if (options[i].first == "dllexport_decl" ) { |
98 | file_options.dllexport_decl = options[i].second; |
99 | } else if (options[i].first == "safe_boundary_check" ) { |
100 | file_options.safe_boundary_check = true; |
101 | } else if (options[i].first == "annotate_headers" ) { |
102 | file_options.annotate_headers = true; |
103 | } else if (options[i].first == "annotation_pragma_name" ) { |
104 | file_options.annotation_pragma_name = options[i].second; |
105 | } else if (options[i].first == "annotation_guard_name" ) { |
106 | file_options.annotation_guard_name = options[i].second; |
107 | } else if (options[i].first == "speed" ) { |
108 | file_options.enforce_mode = EnforceOptimizeMode::kSpeed; |
109 | } else if (options[i].first == "code_size" ) { |
110 | file_options.enforce_mode = EnforceOptimizeMode::kCodeSize; |
111 | } else if (options[i].first == "lite" ) { |
112 | file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime; |
113 | } else if (options[i].first == "lite_implicit_weak_fields" ) { |
114 | file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime; |
115 | file_options.lite_implicit_weak_fields = true; |
116 | if (!options[i].second.empty()) { |
117 | file_options.num_cc_files = |
118 | strto32(nptr: options[i].second.c_str(), endptr: nullptr, base: 10); |
119 | } |
120 | } else if (options[i].first == "proto_h" ) { |
121 | file_options.proto_h = true; |
122 | } else if (options[i].first == "annotate_accessor" ) { |
123 | file_options.annotate_accessor = true; |
124 | } else if (options[i].first == "inject_field_listener_events" ) { |
125 | file_options.field_listener_options.inject_field_listener_events = true; |
126 | } else if (options[i].first == "forbidden_field_listener_events" ) { |
127 | std::size_t pos = 0; |
128 | do { |
129 | std::size_t next_pos = options[i].second.find_first_of(s: "+" , pos: pos); |
130 | if (next_pos == std::string::npos) { |
131 | next_pos = options[i].second.size(); |
132 | } |
133 | if (next_pos > pos) |
134 | file_options.field_listener_options.forbidden_field_listener_events |
135 | .insert(x: options[i].second.substr(pos: pos, n: next_pos - pos)); |
136 | pos = next_pos + 1; |
137 | } while (pos < options[i].second.size()); |
138 | } else if (options[i].first == "verified_lazy" ) { |
139 | file_options.unverified_lazy = false; |
140 | } else if (options[i].first == "unverified_lazy_message_sets" ) { |
141 | file_options.unverified_lazy_message_sets = true; |
142 | } else if (options[i].first == "message_owned_arena_trial" ) { |
143 | file_options.message_owned_arena_trial = true; |
144 | } else if (options[i].first == "force_eagerly_verified_lazy" ) { |
145 | file_options.force_eagerly_verified_lazy = true; |
146 | } else if (options[i].first == "experimental_tail_call_table_mode" ) { |
147 | if (options[i].second == "never" ) { |
148 | file_options.tctable_mode = Options::kTCTableNever; |
149 | } else if (options[i].second == "guarded" ) { |
150 | file_options.tctable_mode = Options::kTCTableGuarded; |
151 | } else if (options[i].second == "always" ) { |
152 | file_options.tctable_mode = Options::kTCTableAlways; |
153 | } else { |
154 | *error = "Unknown value for experimental_tail_call_table_mode: " + |
155 | options[i].second; |
156 | return false; |
157 | } |
158 | } else { |
159 | *error = "Unknown generator option: " + options[i].first; |
160 | return false; |
161 | } |
162 | } |
163 | |
164 | // The safe_boundary_check option controls behavior for Google-internal |
165 | // protobuf APIs. |
166 | if (file_options.safe_boundary_check && file_options.opensource_runtime) { |
167 | *error = |
168 | "The safe_boundary_check option is not supported outside of Google." ; |
169 | return false; |
170 | } |
171 | |
172 | // ----------------------------------------------------------------- |
173 | |
174 | |
175 | std::string basename = StripProto(filename: file->name()); |
176 | |
177 | if (MaybeBootstrap(options: file_options, generator_context, bootstrap_flag: file_options.bootstrap, |
178 | basename: &basename)) { |
179 | return true; |
180 | } |
181 | |
182 | FileGenerator file_generator(file, file_options); |
183 | |
184 | // Generate header(s). |
185 | if (file_options.proto_h) { |
186 | std::unique_ptr<io::ZeroCopyOutputStream> output( |
187 | generator_context->Open(filename: basename + ".proto.h" )); |
188 | GeneratedCodeInfo annotations; |
189 | io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector( |
190 | &annotations); |
191 | std::string info_path = basename + ".proto.h.meta" ; |
192 | io::Printer printer( |
193 | output.get(), '$', |
194 | file_options.annotate_headers ? &annotation_collector : nullptr); |
195 | file_generator.GenerateProtoHeader( |
196 | printer: &printer, info_path: file_options.annotate_headers ? info_path : "" ); |
197 | if (file_options.annotate_headers) { |
198 | std::unique_ptr<io::ZeroCopyOutputStream> info_output( |
199 | generator_context->Open(filename: info_path)); |
200 | annotations.SerializeToZeroCopyStream(output: info_output.get()); |
201 | } |
202 | } |
203 | |
204 | { |
205 | std::unique_ptr<io::ZeroCopyOutputStream> output( |
206 | generator_context->Open(filename: basename + ".pb.h" )); |
207 | GeneratedCodeInfo annotations; |
208 | io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector( |
209 | &annotations); |
210 | std::string info_path = basename + ".pb.h.meta" ; |
211 | io::Printer printer( |
212 | output.get(), '$', |
213 | file_options.annotate_headers ? &annotation_collector : nullptr); |
214 | file_generator.GeneratePBHeader( |
215 | printer: &printer, info_path: file_options.annotate_headers ? info_path : "" ); |
216 | if (file_options.annotate_headers) { |
217 | std::unique_ptr<io::ZeroCopyOutputStream> info_output( |
218 | generator_context->Open(filename: info_path)); |
219 | annotations.SerializeToZeroCopyStream(output: info_output.get()); |
220 | } |
221 | } |
222 | |
223 | // Generate cc file(s). |
224 | if (UsingImplicitWeakFields(file, options: file_options)) { |
225 | { |
226 | // This is the global .cc file, containing |
227 | // enum/services/tables/reflection |
228 | std::unique_ptr<io::ZeroCopyOutputStream> output( |
229 | generator_context->Open(filename: basename + ".pb.cc" )); |
230 | io::Printer printer(output.get(), '$'); |
231 | file_generator.GenerateGlobalSource(printer: &printer); |
232 | } |
233 | |
234 | int num_cc_files = |
235 | file_generator.NumMessages() + file_generator.NumExtensions(); |
236 | |
237 | // If we're using implicit weak fields then we allow the user to |
238 | // optionally specify how many files to generate, not counting the global |
239 | // pb.cc file. If we have more files than messages, then some files will |
240 | // be generated as empty placeholders. |
241 | if (file_options.num_cc_files > 0) { |
242 | GOOGLE_CHECK_LE(num_cc_files, file_options.num_cc_files) |
243 | << "There must be at least as many numbered .cc files as messages " |
244 | "and extensions." ; |
245 | num_cc_files = file_options.num_cc_files; |
246 | } |
247 | int cc_file_number = 0; |
248 | for (int i = 0; i < file_generator.NumMessages(); i++) { |
249 | std::unique_ptr<io::ZeroCopyOutputStream> output(generator_context->Open( |
250 | filename: NumberedCcFileName(basename, number: cc_file_number++))); |
251 | io::Printer printer(output.get(), '$'); |
252 | file_generator.GenerateSourceForMessage(idx: i, printer: &printer); |
253 | } |
254 | for (int i = 0; i < file_generator.NumExtensions(); i++) { |
255 | std::unique_ptr<io::ZeroCopyOutputStream> output(generator_context->Open( |
256 | filename: NumberedCcFileName(basename, number: cc_file_number++))); |
257 | io::Printer printer(output.get(), '$'); |
258 | file_generator.GenerateSourceForExtension(idx: i, printer: &printer); |
259 | } |
260 | // Create empty placeholder files if necessary to match the expected number |
261 | // of files. |
262 | for (; cc_file_number < num_cc_files; ++cc_file_number) { |
263 | std::unique_ptr<io::ZeroCopyOutputStream> output(generator_context->Open( |
264 | filename: NumberedCcFileName(basename, number: cc_file_number))); |
265 | } |
266 | } else { |
267 | std::unique_ptr<io::ZeroCopyOutputStream> output( |
268 | generator_context->Open(filename: basename + ".pb.cc" )); |
269 | io::Printer printer(output.get(), '$'); |
270 | file_generator.GenerateSource(printer: &printer); |
271 | } |
272 | |
273 | return true; |
274 | } |
275 | |
276 | } // namespace cpp |
277 | } // namespace compiler |
278 | } // namespace protobuf |
279 | } // namespace google |
280 | |