Line data Source code
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/command_line_interface.h>
36 :
37 : #include <stdio.h>
38 : #include <sys/types.h>
39 : #include <sys/stat.h>
40 : #include <fcntl.h>
41 : #ifdef _MSC_VER
42 : #include <io.h>
43 : #include <direct.h>
44 : #else
45 : #include <unistd.h>
46 : #endif
47 : #include <errno.h>
48 : #include <iostream>
49 : #include <ctype.h>
50 :
51 : #include <memory>
52 : #ifndef _SHARED_PTR_H
53 : #include <google/protobuf/stubs/shared_ptr.h>
54 : #endif
55 :
56 : #ifdef __APPLE__
57 : #include <mach-o/dyld.h>
58 : #endif
59 :
60 : #include <google/protobuf/stubs/common.h>
61 : #include <google/protobuf/stubs/stringprintf.h>
62 : #include <google/protobuf/compiler/importer.h>
63 : #include <google/protobuf/compiler/code_generator.h>
64 : #include <google/protobuf/compiler/plugin.pb.h>
65 : #include <google/protobuf/compiler/subprocess.h>
66 : #include <google/protobuf/compiler/zip_writer.h>
67 : #include <google/protobuf/descriptor.h>
68 : #include <google/protobuf/text_format.h>
69 : #include <google/protobuf/dynamic_message.h>
70 : #include <google/protobuf/io/coded_stream.h>
71 : #include <google/protobuf/io/zero_copy_stream_impl.h>
72 : #include <google/protobuf/io/printer.h>
73 : #include <google/protobuf/stubs/logging.h>
74 : #include <google/protobuf/stubs/strutil.h>
75 : #include <google/protobuf/stubs/substitute.h>
76 : #include <google/protobuf/stubs/map_util.h>
77 : #include <google/protobuf/stubs/stl_util.h>
78 :
79 :
80 : namespace google {
81 : namespace protobuf {
82 : namespace compiler {
83 :
84 : #if defined(_WIN32)
85 : #define mkdir(name, mode) mkdir(name)
86 : #ifndef W_OK
87 : #define W_OK 02 // not defined by MSVC for whatever reason
88 : #endif
89 : #ifndef F_OK
90 : #define F_OK 00 // not defined by MSVC for whatever reason
91 : #endif
92 : #ifndef STDIN_FILENO
93 : #define STDIN_FILENO 0
94 : #endif
95 : #ifndef STDOUT_FILENO
96 : #define STDOUT_FILENO 1
97 : #endif
98 : #endif
99 :
100 : #ifndef O_BINARY
101 : #ifdef _O_BINARY
102 : #define O_BINARY _O_BINARY
103 : #else
104 : #define O_BINARY 0 // If this isn't defined, the platform doesn't need it.
105 : #endif
106 : #endif
107 :
108 : namespace {
109 : #if defined(_WIN32) && !defined(__CYGWIN__)
110 : static const char* kPathSeparator = ";";
111 : #else
112 : static const char* kPathSeparator = ":";
113 : #endif
114 :
115 : // Returns true if the text looks like a Windows-style absolute path, starting
116 : // with a drive letter. Example: "C:\foo". TODO(kenton): Share this with
117 : // copy in importer.cc?
118 : static bool IsWindowsAbsolutePath(const string& text) {
119 : #if defined(_WIN32) || defined(__CYGWIN__)
120 : return text.size() >= 3 && text[1] == ':' &&
121 : isalpha(text[0]) &&
122 : (text[2] == '/' || text[2] == '\\') &&
123 : text.find_last_of(':') == 1;
124 : #else
125 : return false;
126 : #endif
127 : }
128 :
129 : void SetFdToTextMode(int fd) {
130 : #ifdef _WIN32
131 : if (_setmode(fd, _O_TEXT) == -1) {
132 : // This should never happen, I think.
133 : GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_TEXT): " << strerror(errno);
134 : }
135 : #endif
136 : // (Text and binary are the same on non-Windows platforms.)
137 : }
138 :
139 : void SetFdToBinaryMode(int fd) {
140 : #ifdef _WIN32
141 : if (_setmode(fd, _O_BINARY) == -1) {
142 : // This should never happen, I think.
143 : GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_BINARY): " << strerror(errno);
144 : }
145 : #endif
146 : // (Text and binary are the same on non-Windows platforms.)
147 : }
148 :
149 17 : void AddTrailingSlash(string* path) {
150 17 : if (!path->empty() && path->at(path->size() - 1) != '/') {
151 17 : path->push_back('/');
152 : }
153 17 : }
154 :
155 17 : bool VerifyDirectoryExists(const string& path) {
156 17 : if (path.empty()) return true;
157 :
158 17 : if (access(path.c_str(), F_OK) == -1) {
159 0 : std::cerr << path << ": " << strerror(errno) << std::endl;
160 0 : return false;
161 : } else {
162 : return true;
163 : }
164 : }
165 :
166 : // Try to create the parent directory of the given file, creating the parent's
167 : // parent if necessary, and so on. The full file name is actually
168 : // (prefix + filename), but we assume |prefix| already exists and only create
169 : // directories listed in |filename|.
170 114 : bool TryCreateParentDirectory(const string& prefix, const string& filename) {
171 : // Recursively create parent directories to the output file.
172 114 : vector<string> parts = Split(filename, "/", true);
173 114 : string path_so_far = prefix;
174 738 : for (int i = 0; i < parts.size() - 1; i++) {
175 624 : path_so_far += parts[i];
176 312 : if (mkdir(path_so_far.c_str(), 0777) != 0) {
177 312 : if (errno != EEXIST) {
178 0 : std::cerr << filename << ": while trying to create directory "
179 0 : << path_so_far << ": " << strerror(errno) << std::endl;
180 : return false;
181 : }
182 : }
183 : path_so_far += '/';
184 : }
185 :
186 114 : return true;
187 : }
188 :
189 : // Get the absolute path of this protoc binary.
190 17 : bool GetProtocAbsolutePath(string* path) {
191 : #ifdef _WIN32
192 : char buffer[MAX_PATH];
193 : int len = GetModuleFileNameA(NULL, buffer, MAX_PATH);
194 : #elif __APPLE__
195 : char buffer[PATH_MAX];
196 : int len = 0;
197 :
198 : char dirtybuffer[PATH_MAX];
199 : uint32_t size = sizeof(dirtybuffer);
200 : if (_NSGetExecutablePath(dirtybuffer, &size) == 0) {
201 : realpath(dirtybuffer, buffer);
202 : len = strlen(buffer);
203 : }
204 : #else
205 : char buffer[PATH_MAX];
206 17 : int len = readlink("/proc/self/exe", buffer, PATH_MAX);
207 : #endif
208 17 : if (len > 0) {
209 17 : path->assign(buffer, len);
210 : return true;
211 : } else {
212 : return false;
213 : }
214 : }
215 :
216 : // Whether a path is where google/protobuf/descriptor.proto and other well-known
217 : // type protos are installed.
218 49 : bool IsInstalledProtoPath(const string& path) {
219 : // Checking the descriptor.proto file should be good enough.
220 49 : string file_path = path + "/google/protobuf/descriptor.proto";
221 98 : return access(file_path.c_str(), F_OK) != -1;
222 : }
223 :
224 : // Add the paths where google/protobuf/descritor.proto and other well-known
225 : // type protos are installed.
226 17 : void AddDefaultProtoPaths(vector<pair<string, string> >* paths) {
227 : // TODO(xiaofeng): The code currently only checks relative paths of where
228 : // the protoc binary is installed. We probably should make it handle more
229 : // cases than that.
230 : string path;
231 17 : if (!GetProtocAbsolutePath(&path)) {
232 : return;
233 : }
234 : // Strip the binary name.
235 17 : size_t pos = path.find_last_of("/\\");
236 17 : if (pos == string::npos || pos == 0) {
237 : return;
238 : }
239 34 : path = path.substr(0, pos);
240 : // Check the binary's directory.
241 17 : if (IsInstalledProtoPath(path)) {
242 2 : paths->push_back(pair<string, string>("", path));
243 1 : return;
244 : }
245 : // Check if there is an include subdirectory.
246 32 : if (IsInstalledProtoPath(path + "/include")) {
247 0 : paths->push_back(pair<string, string>("", path + "/include"));
248 0 : return;
249 : }
250 : // Check if the upper level directory has an "include" subdirectory.
251 16 : pos = path.find_last_of("/\\");
252 16 : if (pos == string::npos || pos == 0) {
253 : return;
254 : }
255 32 : path = path.substr(0, pos);
256 32 : if (IsInstalledProtoPath(path + "/include")) {
257 0 : paths->push_back(pair<string, string>("", path + "/include"));
258 0 : return;
259 : }
260 : }
261 : } // namespace
262 :
263 : // A MultiFileErrorCollector that prints errors to stderr.
264 : class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
265 : public io::ErrorCollector {
266 : public:
267 : ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL)
268 51 : : format_(format), tree_(tree) {}
269 17 : ~ErrorPrinter() {}
270 :
271 : // implements MultiFileErrorCollector ------------------------------
272 0 : void AddError(const string& filename, int line, int column,
273 : const string& message) {
274 :
275 : // Print full path when running under MSVS
276 : string dfile;
277 0 : if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
278 0 : tree_ != NULL &&
279 0 : tree_->VirtualFileToDiskFile(filename, &dfile)) {
280 : std::cerr << dfile;
281 : } else {
282 : std::cerr << filename;
283 : }
284 :
285 : // Users typically expect 1-based line/column numbers, so we add 1
286 : // to each here.
287 0 : if (line != -1) {
288 : // Allow for both GCC- and Visual-Studio-compatible output.
289 0 : switch (format_) {
290 : case CommandLineInterface::ERROR_FORMAT_GCC:
291 0 : std::cerr << ":" << (line + 1) << ":" << (column + 1);
292 : break;
293 : case CommandLineInterface::ERROR_FORMAT_MSVS:
294 0 : std::cerr << "(" << (line + 1)
295 0 : << ") : error in column=" << (column + 1);
296 : break;
297 : }
298 : }
299 :
300 0 : std::cerr << ": " << message << std::endl;
301 0 : }
302 :
303 : // implements io::ErrorCollector -----------------------------------
304 0 : void AddError(int line, int column, const string& message) {
305 0 : AddError("input", line, column, message);
306 0 : }
307 :
308 : private:
309 : const ErrorFormat format_;
310 : DiskSourceTree *tree_;
311 : };
312 :
313 : // -------------------------------------------------------------------
314 :
315 : // A GeneratorContext implementation that buffers files in memory, then dumps
316 : // them all to disk on demand.
317 : class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
318 : public:
319 : GeneratorContextImpl(const vector<const FileDescriptor*>& parsed_files);
320 : ~GeneratorContextImpl();
321 :
322 : // Write all files in the directory to disk at the given output location,
323 : // which must end in a '/'.
324 : bool WriteAllToDisk(const string& prefix);
325 :
326 : // Write the contents of this directory to a ZIP-format archive with the
327 : // given name.
328 : bool WriteAllToZip(const string& filename);
329 :
330 : // Add a boilerplate META-INF/MANIFEST.MF file as required by the Java JAR
331 : // format, unless one has already been written.
332 : void AddJarManifest();
333 :
334 : // Get name of all output files.
335 : void GetOutputFilenames(vector<string>* output_filenames);
336 :
337 : // implements GeneratorContext --------------------------------------
338 : io::ZeroCopyOutputStream* Open(const string& filename);
339 : io::ZeroCopyOutputStream* OpenForAppend(const string& filename);
340 : io::ZeroCopyOutputStream* OpenForInsert(
341 : const string& filename, const string& insertion_point);
342 0 : void ListParsedFiles(vector<const FileDescriptor*>* output) {
343 0 : *output = parsed_files_;
344 0 : }
345 :
346 : private:
347 : friend class MemoryOutputStream;
348 :
349 : // map instead of hash_map so that files are written in order (good when
350 : // writing zips).
351 : map<string, string*> files_;
352 : const vector<const FileDescriptor*>& parsed_files_;
353 : bool had_error_;
354 : };
355 :
356 : class CommandLineInterface::MemoryOutputStream
357 : : public io::ZeroCopyOutputStream {
358 : public:
359 : MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
360 : bool append_mode);
361 : MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
362 : const string& insertion_point);
363 : virtual ~MemoryOutputStream();
364 :
365 : // implements ZeroCopyOutputStream ---------------------------------
366 3176 : virtual bool Next(void** data, int* size) { return inner_->Next(data, size); }
367 228 : virtual void BackUp(int count) { inner_->BackUp(count); }
368 0 : virtual int64 ByteCount() const { return inner_->ByteCount(); }
369 :
370 : private:
371 : // Where to insert the string when it's done.
372 : GeneratorContextImpl* directory_;
373 : string filename_;
374 : string insertion_point_;
375 :
376 : // The string we're building.
377 : string data_;
378 :
379 : // Whether we should append the output stream to the existing file.
380 : bool append_mode_;
381 :
382 : // StringOutputStream writing to data_.
383 : google::protobuf::scoped_ptr<io::StringOutputStream> inner_;
384 : };
385 :
386 : // -------------------------------------------------------------------
387 :
388 17 : CommandLineInterface::GeneratorContextImpl::GeneratorContextImpl(
389 : const vector<const FileDescriptor*>& parsed_files)
390 : : parsed_files_(parsed_files),
391 51 : had_error_(false) {
392 17 : }
393 :
394 68 : CommandLineInterface::GeneratorContextImpl::~GeneratorContextImpl() {
395 17 : STLDeleteValues(&files_);
396 34 : }
397 :
398 17 : bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk(
399 : const string& prefix) {
400 17 : if (had_error_) {
401 : return false;
402 : }
403 :
404 17 : if (!VerifyDirectoryExists(prefix)) {
405 : return false;
406 : }
407 :
408 182 : for (map<string, string*>::const_iterator iter = files_.begin();
409 262 : iter != files_.end(); ++iter) {
410 114 : const string& relative_filename = iter->first;
411 228 : const char* data = iter->second->data();
412 228 : int size = iter->second->size();
413 :
414 114 : if (!TryCreateParentDirectory(prefix, relative_filename)) {
415 0 : return false;
416 : }
417 114 : string filename = prefix + relative_filename;
418 :
419 : // Create the output file.
420 : int file_descriptor;
421 114 : do {
422 : file_descriptor =
423 228 : open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
424 0 : } while (file_descriptor < 0 && errno == EINTR);
425 :
426 114 : if (file_descriptor < 0) {
427 0 : int error = errno;
428 0 : std::cerr << filename << ": " << strerror(error);
429 : return false;
430 : }
431 :
432 : // Write the file.
433 228 : while (size > 0) {
434 : int write_result;
435 114 : do {
436 114 : write_result = write(file_descriptor, data, size);
437 0 : } while (write_result < 0 && errno == EINTR);
438 :
439 114 : if (write_result <= 0) {
440 : // Write error.
441 :
442 : // FIXME(kenton): According to the man page, if write() returns zero,
443 : // there was no error; write() simply did not write anything. It's
444 : // unclear under what circumstances this might happen, but presumably
445 : // errno won't be set in this case. I am confused as to how such an
446 : // event should be handled. For now I'm treating it as an error,
447 : // since retrying seems like it could lead to an infinite loop. I
448 : // suspect this never actually happens anyway.
449 :
450 0 : if (write_result < 0) {
451 0 : int error = errno;
452 0 : std::cerr << filename << ": write: " << strerror(error);
453 : } else {
454 0 : std::cerr << filename << ": write() returned zero?" << std::endl;
455 : }
456 : return false;
457 : }
458 :
459 114 : data += write_result;
460 114 : size -= write_result;
461 : }
462 :
463 114 : if (close(file_descriptor) != 0) {
464 0 : int error = errno;
465 0 : std::cerr << filename << ": close: " << strerror(error);
466 : return false;
467 : }
468 : }
469 :
470 : return true;
471 : }
472 :
473 0 : bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip(
474 : const string& filename) {
475 0 : if (had_error_) {
476 : return false;
477 : }
478 :
479 : // Create the output file.
480 : int file_descriptor;
481 0 : do {
482 : file_descriptor =
483 0 : open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
484 0 : } while (file_descriptor < 0 && errno == EINTR);
485 :
486 0 : if (file_descriptor < 0) {
487 0 : int error = errno;
488 0 : std::cerr << filename << ": " << strerror(error);
489 : return false;
490 : }
491 :
492 : // Create the ZipWriter
493 0 : io::FileOutputStream stream(file_descriptor);
494 0 : ZipWriter zip_writer(&stream);
495 :
496 0 : for (map<string, string*>::const_iterator iter = files_.begin();
497 0 : iter != files_.end(); ++iter) {
498 0 : zip_writer.Write(iter->first, *iter->second);
499 : }
500 :
501 0 : zip_writer.WriteDirectory();
502 :
503 0 : if (stream.GetErrno() != 0) {
504 0 : std::cerr << filename << ": " << strerror(stream.GetErrno()) << std::endl;
505 : }
506 :
507 0 : if (!stream.Close()) {
508 0 : std::cerr << filename << ": " << strerror(stream.GetErrno()) << std::endl;
509 : }
510 :
511 0 : return true;
512 : }
513 :
514 0 : void CommandLineInterface::GeneratorContextImpl::AddJarManifest() {
515 0 : string** map_slot = &files_["META-INF/MANIFEST.MF"];
516 0 : if (*map_slot == NULL) {
517 : *map_slot = new string(
518 : "Manifest-Version: 1.0\n"
519 : "Created-By: 1.6.0 (protoc)\n"
520 0 : "\n");
521 : }
522 0 : }
523 :
524 0 : void CommandLineInterface::GeneratorContextImpl::GetOutputFilenames(
525 : vector<string>* output_filenames) {
526 0 : for (map<string, string*>::iterator iter = files_.begin();
527 0 : iter != files_.end(); ++iter) {
528 0 : output_filenames->push_back(iter->first);
529 : }
530 0 : }
531 :
532 114 : io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open(
533 : const string& filename) {
534 114 : return new MemoryOutputStream(this, filename, false);
535 : }
536 :
537 : io::ZeroCopyOutputStream*
538 0 : CommandLineInterface::GeneratorContextImpl::OpenForAppend(
539 : const string& filename) {
540 0 : return new MemoryOutputStream(this, filename, true);
541 : }
542 :
543 : io::ZeroCopyOutputStream*
544 0 : CommandLineInterface::GeneratorContextImpl::OpenForInsert(
545 : const string& filename, const string& insertion_point) {
546 0 : return new MemoryOutputStream(this, filename, insertion_point);
547 : }
548 :
549 : // -------------------------------------------------------------------
550 :
551 114 : CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
552 : GeneratorContextImpl* directory, const string& filename, bool append_mode)
553 : : directory_(directory),
554 : filename_(filename),
555 : append_mode_(append_mode),
556 456 : inner_(new io::StringOutputStream(&data_)) {
557 114 : }
558 :
559 0 : CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
560 : GeneratorContextImpl* directory, const string& filename,
561 : const string& insertion_point)
562 : : directory_(directory),
563 : filename_(filename),
564 : insertion_point_(insertion_point),
565 0 : inner_(new io::StringOutputStream(&data_)) {
566 0 : }
567 :
568 798 : CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
569 : // Make sure all data has been written.
570 114 : inner_.reset();
571 :
572 : // Insert into the directory.
573 114 : string** map_slot = &directory_->files_[filename_];
574 :
575 228 : if (insertion_point_.empty()) {
576 : // This was just a regular Open().
577 114 : if (*map_slot != NULL) {
578 0 : if (append_mode_) {
579 0 : (*map_slot)->append(data_);
580 : } else {
581 0 : std::cerr << filename_ << ": Tried to write the same file twice."
582 : << std::endl;
583 0 : directory_->had_error_ = true;
584 : }
585 : return;
586 : }
587 :
588 228 : *map_slot = new string;
589 114 : (*map_slot)->swap(data_);
590 : } else {
591 : // This was an OpenForInsert().
592 :
593 : // If the data doens't end with a clean line break, add one.
594 0 : if (!data_.empty() && data_[data_.size() - 1] != '\n') {
595 0 : data_.push_back('\n');
596 : }
597 :
598 : // Find the file we are going to insert into.
599 0 : if (*map_slot == NULL) {
600 0 : std::cerr << filename_
601 0 : << ": Tried to insert into file that doesn't exist."
602 : << std::endl;
603 0 : directory_->had_error_ = true;
604 0 : return;
605 : }
606 0 : string* target = *map_slot;
607 :
608 : // Find the insertion point.
609 : string magic_string = strings::Substitute(
610 0 : "@@protoc_insertion_point($0)", insertion_point_);
611 0 : string::size_type pos = target->find(magic_string);
612 :
613 0 : if (pos == string::npos) {
614 0 : std::cerr << filename_ << ": insertion point \"" << insertion_point_
615 0 : << "\" not found." << std::endl;
616 0 : directory_->had_error_ = true;
617 : return;
618 : }
619 :
620 : // Seek backwards to the beginning of the line, which is where we will
621 : // insert the data. Note that this has the effect of pushing the insertion
622 : // point down, so the data is inserted before it. This is intentional
623 : // because it means that multiple insertions at the same point will end
624 : // up in the expected order in the final output.
625 0 : pos = target->find_last_of('\n', pos);
626 0 : if (pos == string::npos) {
627 : // Insertion point is on the first line.
628 : pos = 0;
629 : } else {
630 : // Advance to character after '\n'.
631 0 : ++pos;
632 : }
633 :
634 : // Extract indent.
635 0 : string indent_(*target, pos, target->find_first_not_of(" \t", pos) - pos);
636 :
637 0 : if (indent_.empty()) {
638 : // No indent. This makes things easier.
639 0 : target->insert(pos, data_);
640 : } else {
641 : // Calculate how much space we need.
642 : int indent_size = 0;
643 0 : for (int i = 0; i < data_.size(); i++) {
644 0 : if (data_[i] == '\n') indent_size += indent_.size();
645 : }
646 :
647 : // Make a hole for it.
648 0 : target->insert(pos, data_.size() + indent_size, '\0');
649 :
650 : // Now copy in the data.
651 0 : string::size_type data_pos = 0;
652 0 : char* target_ptr = string_as_array(target) + pos;
653 0 : while (data_pos < data_.size()) {
654 : // Copy indent.
655 0 : memcpy(target_ptr, indent_.data(), indent_.size());
656 0 : target_ptr += indent_.size();
657 :
658 : // Copy line from data_.
659 : // We already guaranteed that data_ ends with a newline (above), so this
660 : // search can't fail.
661 : string::size_type line_length =
662 0 : data_.find_first_of('\n', data_pos) + 1 - data_pos;
663 0 : memcpy(target_ptr, data_.data() + data_pos, line_length);
664 0 : target_ptr += line_length;
665 0 : data_pos += line_length;
666 : }
667 :
668 0 : GOOGLE_CHECK_EQ(target_ptr,
669 0 : string_as_array(target) + pos + data_.size() + indent_size);
670 : }
671 : }
672 342 : }
673 :
674 : // ===================================================================
675 :
676 17 : CommandLineInterface::CommandLineInterface()
677 : : mode_(MODE_COMPILE),
678 : print_mode_(PRINT_NONE),
679 : error_format_(ERROR_FORMAT_GCC),
680 : imports_in_descriptor_set_(false),
681 : source_info_in_descriptor_set_(false),
682 : disallow_services_(false),
683 238 : inputs_are_proto_path_relative_(false) {}
684 187 : CommandLineInterface::~CommandLineInterface() {}
685 :
686 102 : void CommandLineInterface::RegisterGenerator(const string& flag_name,
687 : CodeGenerator* generator,
688 : const string& help_text) {
689 : GeneratorInfo info;
690 : info.flag_name = flag_name;
691 102 : info.generator = generator;
692 : info.help_text = help_text;
693 102 : generators_by_flag_name_[flag_name] = info;
694 102 : }
695 :
696 17 : void CommandLineInterface::RegisterGenerator(const string& flag_name,
697 : const string& option_flag_name,
698 : CodeGenerator* generator,
699 : const string& help_text) {
700 : GeneratorInfo info;
701 : info.flag_name = flag_name;
702 : info.option_flag_name = option_flag_name;
703 17 : info.generator = generator;
704 : info.help_text = help_text;
705 17 : generators_by_flag_name_[flag_name] = info;
706 17 : generators_by_option_name_[option_flag_name] = info;
707 17 : }
708 :
709 17 : void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) {
710 17 : plugin_prefix_ = exe_name_prefix;
711 17 : }
712 :
713 17 : int CommandLineInterface::Run(int argc, const char* const argv[]) {
714 17 : Clear();
715 17 : switch (ParseArguments(argc, argv)) {
716 : case PARSE_ARGUMENT_DONE_AND_EXIT:
717 : return 0;
718 : case PARSE_ARGUMENT_FAIL:
719 0 : return 1;
720 : case PARSE_ARGUMENT_DONE_AND_CONTINUE:
721 : break;
722 : }
723 :
724 87 : AddDefaultProtoPaths(&proto_path_);
725 :
726 : // Set up the source tree.
727 17 : DiskSourceTree source_tree;
728 123 : for (int i = 0; i < proto_path_.size(); i++) {
729 36 : source_tree.MapPath(proto_path_[i].first, proto_path_[i].second);
730 : }
731 :
732 : // Map input files to virtual paths if necessary.
733 17 : if (!inputs_are_proto_path_relative_) {
734 17 : if (!MakeInputsBeProtoPathRelative(&source_tree)) {
735 : return 1;
736 : }
737 : }
738 :
739 : // Allocate the Importer.
740 34 : ErrorPrinter error_collector(error_format_, &source_tree);
741 34 : Importer importer(&source_tree, &error_collector);
742 :
743 : vector<const FileDescriptor*> parsed_files;
744 :
745 : // Parse each file.
746 370 : for (int i = 0; i < input_files_.size(); i++) {
747 : // Import the file.
748 319 : importer.AddUnusedImportTrackFile(input_files_[i]);
749 171 : const FileDescriptor* parsed_file = importer.Import(input_files_[i]);
750 57 : importer.ClearUnusedImportTrackFiles();
751 57 : if (parsed_file == NULL) return 1;
752 57 : parsed_files.push_back(parsed_file);
753 :
754 : // Enforce --disallow_services.
755 57 : if (disallow_services_ && parsed_file->service_count() > 0) {
756 0 : cerr << parsed_file->name() << ": This file contains services, but "
757 0 : "--disallow_services was used." << endl;
758 : return 1;
759 : }
760 : }
761 :
762 : // We construct a separate GeneratorContext for each output location. Note
763 : // that two code generators may output to the same location, in which case
764 : // they should share a single GeneratorContext so that OpenForInsert() works.
765 17 : GeneratorContextMap output_directories;
766 :
767 : // Generate output.
768 17 : if (mode_ == MODE_COMPILE) {
769 119 : for (int i = 0; i < output_directives_.size(); i++) {
770 119 : string output_location = output_directives_[i].output_location;
771 51 : if (!HasSuffixString(output_location, ".zip") &&
772 51 : !HasSuffixString(output_location, ".jar")) {
773 17 : AddTrailingSlash(&output_location);
774 : }
775 17 : GeneratorContextImpl** map_slot = &output_directories[output_location];
776 :
777 17 : if (*map_slot == NULL) {
778 : // First time we've seen this output location.
779 17 : *map_slot = new GeneratorContextImpl(parsed_files);
780 : }
781 :
782 51 : if (!GenerateOutput(parsed_files, output_directives_[i], *map_slot)) {
783 0 : STLDeleteValues(&output_directories);
784 0 : return 1;
785 : }
786 : }
787 : }
788 :
789 : // Write all output to disk.
790 68 : for (GeneratorContextMap::iterator iter = output_directories.begin();
791 68 : iter != output_directories.end(); ++iter) {
792 34 : const string& location = iter->first;
793 34 : GeneratorContextImpl* directory = iter->second;
794 34 : if (HasSuffixString(location, "/")) {
795 17 : if (!directory->WriteAllToDisk(location)) {
796 0 : STLDeleteValues(&output_directories);
797 : return 1;
798 : }
799 : } else {
800 0 : if (HasSuffixString(location, ".jar")) {
801 0 : directory->AddJarManifest();
802 : }
803 :
804 0 : if (!directory->WriteAllToZip(location)) {
805 0 : STLDeleteValues(&output_directories);
806 : return 1;
807 : }
808 : }
809 : }
810 :
811 34 : if (!dependency_out_name_.empty()) {
812 0 : if (!GenerateDependencyManifestFile(parsed_files, output_directories,
813 0 : &source_tree)) {
814 : return 1;
815 : }
816 : }
817 :
818 17 : STLDeleteValues(&output_directories);
819 :
820 34 : if (!descriptor_set_name_.empty()) {
821 0 : if (!WriteDescriptorSet(parsed_files)) {
822 : return 1;
823 : }
824 : }
825 :
826 17 : if (mode_ == MODE_ENCODE || mode_ == MODE_DECODE) {
827 0 : if (codec_type_.empty()) {
828 : // HACK: Define an EmptyMessage type to use for decoding.
829 0 : DescriptorPool pool;
830 0 : FileDescriptorProto file;
831 0 : file.set_name("empty_message.proto");
832 0 : file.add_message_type()->set_name("EmptyMessage");
833 0 : GOOGLE_CHECK(pool.BuildFile(file) != NULL);
834 0 : codec_type_ = "EmptyMessage";
835 0 : if (!EncodeOrDecode(&pool)) {
836 0 : return 1;
837 0 : }
838 : } else {
839 0 : if (!EncodeOrDecode(importer.pool())) {
840 : return 1;
841 : }
842 : }
843 : }
844 :
845 17 : if (mode_ == MODE_PRINT) {
846 0 : switch (print_mode_) {
847 : case PRINT_FREE_FIELDS:
848 0 : for (int i = 0; i < parsed_files.size(); ++i) {
849 0 : const FileDescriptor* fd = parsed_files[i];
850 0 : for (int j = 0; j < fd->message_type_count(); ++j) {
851 0 : PrintFreeFieldNumbers(fd->message_type(j));
852 : }
853 : }
854 : break;
855 : case PRINT_NONE:
856 0 : GOOGLE_LOG(ERROR) << "If the code reaches here, it usually means a bug of "
857 0 : "flag parsing in the CommonadLineInterface.";
858 0 : return 1;
859 :
860 : // Do not add a default case.
861 : }
862 : }
863 :
864 17 : return 0;
865 : }
866 :
867 17 : void CommandLineInterface::Clear() {
868 : // Clear all members that are set by Run(). Note that we must not clear
869 : // members which are set by other methods before Run() is called.
870 17 : executable_name_.clear();
871 17 : proto_path_.clear();
872 17 : input_files_.clear();
873 17 : output_directives_.clear();
874 17 : codec_type_.clear();
875 17 : descriptor_set_name_.clear();
876 17 : dependency_out_name_.clear();
877 :
878 17 : mode_ = MODE_COMPILE;
879 17 : print_mode_ = PRINT_NONE;
880 17 : imports_in_descriptor_set_ = false;
881 17 : source_info_in_descriptor_set_ = false;
882 17 : disallow_services_ = false;
883 17 : }
884 :
885 17 : bool CommandLineInterface::MakeInputsBeProtoPathRelative(
886 : DiskSourceTree* source_tree) {
887 370 : for (int i = 0; i < input_files_.size(); i++) {
888 : string virtual_file, shadowing_disk_file;
889 57 : switch (source_tree->DiskFileToVirtualFile(
890 319 : input_files_[i], &virtual_file, &shadowing_disk_file)) {
891 : case DiskSourceTree::SUCCESS:
892 171 : input_files_[i] = virtual_file;
893 : break;
894 : case DiskSourceTree::SHADOWED:
895 0 : std::cerr << input_files_[i]
896 0 : << ": Input is shadowed in the --proto_path by \""
897 : << shadowing_disk_file
898 : << "\". Either use the latter file as your input or reorder "
899 : "the --proto_path so that the former file's location "
900 0 : "comes first." << std::endl;
901 : return false;
902 : case DiskSourceTree::CANNOT_OPEN:
903 0 : std::cerr << input_files_[i] << ": " << strerror(errno) << std::endl;
904 : return false;
905 : case DiskSourceTree::NO_MAPPING:
906 : // First check if the file exists at all.
907 0 : if (access(input_files_[i].c_str(), F_OK) < 0) {
908 : // File does not even exist.
909 0 : std::cerr << input_files_[i] << ": " << strerror(ENOENT) << std::endl;
910 : } else {
911 : std::cerr
912 0 : << input_files_[i]
913 : << ": File does not reside within any path "
914 : "specified using --proto_path (or -I). You must specify a "
915 : "--proto_path which encompasses this file. Note that the "
916 : "proto_path must be an exact prefix of the .proto file "
917 : "names -- protoc is too dumb to figure out when two paths "
918 : "(e.g. absolute and relative) are equivalent (it's harder "
919 0 : "than you think)." << std::endl;
920 : }
921 : return false;
922 : }
923 : }
924 :
925 : return true;
926 : }
927 :
928 : CommandLineInterface::ParseArgumentStatus
929 17 : CommandLineInterface::ParseArguments(int argc, const char* const argv[]) {
930 17 : executable_name_ = argv[0];
931 :
932 : // Iterate through all arguments and parse them.
933 117 : for (int i = 1; i < argc; i++) {
934 : string name, value;
935 :
936 83 : if (ParseArgument(argv[i], &name, &value)) {
937 : // Returned true => Use the next argument as the flag value.
938 0 : if (i + 1 == argc || argv[i+1][0] == '-') {
939 0 : std::cerr << "Missing value for flag: " << name << std::endl;
940 0 : if (name == "--decode") {
941 0 : std::cerr << "To decode an unknown message, use --decode_raw."
942 : << std::endl;
943 : }
944 : return PARSE_ARGUMENT_FAIL;
945 : } else {
946 0 : ++i;
947 0 : value = argv[i];
948 : }
949 : }
950 :
951 83 : ParseArgumentStatus status = InterpretArgument(name, value);
952 83 : if (status != PARSE_ARGUMENT_DONE_AND_CONTINUE)
953 : return status;
954 : }
955 :
956 : // If no --proto_path was given, use the current working directory.
957 34 : if (proto_path_.empty()) {
958 : // Don't use make_pair as the old/default standard library on Solaris
959 : // doesn't support it without explicit template parameters, which are
960 : // incompatible with C++0x's make_pair.
961 48 : proto_path_.push_back(pair<string, string>("", "."));
962 : }
963 :
964 : // Check some errror cases.
965 17 : bool decoding_raw = (mode_ == MODE_DECODE) && codec_type_.empty();
966 17 : if (decoding_raw && !input_files_.empty()) {
967 0 : std::cerr << "When using --decode_raw, no input files should be given."
968 : << std::endl;
969 0 : return PARSE_ARGUMENT_FAIL;
970 34 : } else if (!decoding_raw && input_files_.empty()) {
971 0 : std::cerr << "Missing input file." << std::endl;
972 0 : return PARSE_ARGUMENT_FAIL;
973 : }
974 34 : if (mode_ == MODE_COMPILE && output_directives_.empty() &&
975 0 : descriptor_set_name_.empty()) {
976 0 : std::cerr << "Missing output directives." << std::endl;
977 0 : return PARSE_ARGUMENT_FAIL;
978 : }
979 17 : if (mode_ != MODE_COMPILE && !dependency_out_name_.empty()) {
980 0 : std::cerr << "Can only use --dependency_out=FILE when generating code."
981 : << std::endl;
982 0 : return PARSE_ARGUMENT_FAIL;
983 : }
984 34 : if (!dependency_out_name_.empty() && input_files_.size() > 1) {
985 : std::cerr
986 0 : << "Can only process one input file when using --dependency_out=FILE."
987 : << std::endl;
988 0 : return PARSE_ARGUMENT_FAIL;
989 : }
990 17 : if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) {
991 : std::cerr << "--include_imports only makes sense when combined with "
992 0 : "--descriptor_set_out." << std::endl;
993 : }
994 17 : if (source_info_in_descriptor_set_ && descriptor_set_name_.empty()) {
995 : std::cerr << "--include_source_info only makes sense when combined with "
996 0 : "--descriptor_set_out." << std::endl;
997 : }
998 :
999 : return PARSE_ARGUMENT_DONE_AND_CONTINUE;
1000 : }
1001 :
1002 83 : bool CommandLineInterface::ParseArgument(const char* arg,
1003 : string* name, string* value) {
1004 83 : bool parsed_value = false;
1005 :
1006 83 : if (arg[0] != '-') {
1007 : // Not a flag.
1008 : name->clear();
1009 57 : parsed_value = true;
1010 : *value = arg;
1011 26 : } else if (arg[1] == '-') {
1012 : // Two dashes: Multi-character name, with '=' separating name and
1013 : // value.
1014 25 : const char* equals_pos = strchr(arg, '=');
1015 25 : if (equals_pos != NULL) {
1016 50 : *name = string(arg, equals_pos - arg);
1017 25 : *value = equals_pos + 1;
1018 25 : parsed_value = true;
1019 : } else {
1020 : *name = arg;
1021 : }
1022 : } else {
1023 : // One dash: One-character name, all subsequent characters are the
1024 : // value.
1025 1 : if (arg[1] == '\0') {
1026 : // arg is just "-". We treat this as an input file, except that at
1027 : // present this will just lead to a "file not found" error.
1028 : name->clear();
1029 : *value = arg;
1030 0 : parsed_value = true;
1031 : } else {
1032 2 : *name = string(arg, 2);
1033 1 : *value = arg + 2;
1034 1 : parsed_value = !value->empty();
1035 : }
1036 : }
1037 :
1038 : // Need to return true iff the next arg should be used as the value for this
1039 : // one, false otherwise.
1040 :
1041 83 : if (parsed_value) {
1042 : // We already parsed a value for this flag.
1043 : return false;
1044 : }
1045 :
1046 0 : if (*name == "-h" || *name == "--help" ||
1047 0 : *name == "--disallow_services" ||
1048 0 : *name == "--include_imports" ||
1049 0 : *name == "--include_source_info" ||
1050 0 : *name == "--version" ||
1051 0 : *name == "--decode_raw" ||
1052 : *name == "--print_free_field_numbers") {
1053 : // HACK: These are the only flags that don't take a value.
1054 : // They probably should not be hard-coded like this but for now it's
1055 : // not worth doing better.
1056 : return false;
1057 : }
1058 :
1059 : // Next argument is the flag value.
1060 0 : return true;
1061 : }
1062 :
1063 : CommandLineInterface::ParseArgumentStatus
1064 83 : CommandLineInterface::InterpretArgument(const string& name,
1065 : const string& value) {
1066 83 : if (name.empty()) {
1067 : // Not a flag. Just a filename.
1068 57 : if (value.empty()) {
1069 : std::cerr
1070 : << "You seem to have passed an empty string as one of the "
1071 0 : "arguments to " << executable_name_
1072 : << ". This is actually "
1073 : "sort of hard to do. Congrats. Unfortunately it is not valid "
1074 0 : "input so the program is going to die now." << std::endl;
1075 0 : return PARSE_ARGUMENT_FAIL;
1076 : }
1077 :
1078 57 : input_files_.push_back(value);
1079 :
1080 51 : } else if (name == "-I" || name == "--proto_path") {
1081 : // Java's -classpath (and some other languages) delimits path components
1082 : // with colons. Let's accept that syntax too just to make things more
1083 : // intuitive.
1084 : vector<string> parts = Split(
1085 1 : value, kPathSeparator, true);
1086 :
1087 3 : for (int i = 0; i < parts.size(); i++) {
1088 : string virtual_path;
1089 : string disk_path;
1090 :
1091 3 : string::size_type equals_pos = parts[i].find_first_of('=');
1092 1 : if (equals_pos == string::npos) {
1093 : virtual_path = "";
1094 2 : disk_path = parts[i];
1095 : } else {
1096 0 : virtual_path = parts[i].substr(0, equals_pos);
1097 0 : disk_path = parts[i].substr(equals_pos + 1);
1098 : }
1099 :
1100 1 : if (disk_path.empty()) {
1101 : std::cerr
1102 : << "--proto_path passed empty directory name. (Use \".\" for "
1103 0 : "current directory.)" << std::endl;
1104 0 : return PARSE_ARGUMENT_FAIL;
1105 : }
1106 :
1107 : // Make sure disk path exists, warn otherwise.
1108 1 : if (access(disk_path.c_str(), F_OK) < 0) {
1109 0 : std::cerr << disk_path << ": warning: directory does not exist."
1110 : << std::endl;
1111 : }
1112 :
1113 : // Don't use make_pair as the old/default standard library on Solaris
1114 : // doesn't support it without explicit template parameters, which are
1115 : // incompatible with C++0x's make_pair.
1116 1 : proto_path_.push_back(pair<string, string>(virtual_path, disk_path));
1117 1 : }
1118 :
1119 50 : } else if (name == "-o" || name == "--descriptor_set_out") {
1120 0 : if (!descriptor_set_name_.empty()) {
1121 0 : std::cerr << name << " may only be passed once." << std::endl;
1122 0 : return PARSE_ARGUMENT_FAIL;
1123 : }
1124 0 : if (value.empty()) {
1125 0 : std::cerr << name << " requires a non-empty value." << std::endl;
1126 0 : return PARSE_ARGUMENT_FAIL;
1127 : }
1128 0 : if (mode_ != MODE_COMPILE) {
1129 : std::cerr
1130 : << "Cannot use --encode or --decode and generate descriptors at the "
1131 0 : "same time." << std::endl;
1132 0 : return PARSE_ARGUMENT_FAIL;
1133 : }
1134 0 : descriptor_set_name_ = value;
1135 :
1136 25 : } else if (name == "--dependency_out") {
1137 0 : if (!dependency_out_name_.empty()) {
1138 0 : std::cerr << name << " may only be passed once." << std::endl;
1139 0 : return PARSE_ARGUMENT_FAIL;
1140 : }
1141 0 : if (value.empty()) {
1142 0 : std::cerr << name << " requires a non-empty value." << std::endl;
1143 0 : return PARSE_ARGUMENT_FAIL;
1144 : }
1145 0 : dependency_out_name_ = value;
1146 :
1147 25 : } else if (name == "--include_imports") {
1148 0 : if (imports_in_descriptor_set_) {
1149 0 : std::cerr << name << " may only be passed once." << std::endl;
1150 0 : return PARSE_ARGUMENT_FAIL;
1151 : }
1152 0 : imports_in_descriptor_set_ = true;
1153 :
1154 25 : } else if (name == "--include_source_info") {
1155 0 : if (source_info_in_descriptor_set_) {
1156 0 : std::cerr << name << " may only be passed once." << std::endl;
1157 0 : return PARSE_ARGUMENT_FAIL;
1158 : }
1159 0 : source_info_in_descriptor_set_ = true;
1160 :
1161 50 : } else if (name == "-h" || name == "--help") {
1162 0 : PrintHelpText();
1163 0 : return PARSE_ARGUMENT_DONE_AND_EXIT; // Exit without running compiler.
1164 :
1165 25 : } else if (name == "--version") {
1166 0 : if (!version_info_.empty()) {
1167 0 : std::cout << version_info_ << std::endl;
1168 : }
1169 0 : cout << "libprotoc "
1170 0 : << protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION)
1171 : << endl;
1172 0 : return PARSE_ARGUMENT_DONE_AND_EXIT; // Exit without running compiler.
1173 :
1174 25 : } else if (name == "--disallow_services") {
1175 0 : disallow_services_ = true;
1176 :
1177 75 : } else if (name == "--encode" || name == "--decode" ||
1178 : name == "--decode_raw") {
1179 0 : if (mode_ != MODE_COMPILE) {
1180 0 : std::cerr << "Only one of --encode and --decode can be specified."
1181 : << std::endl;
1182 0 : return PARSE_ARGUMENT_FAIL;
1183 : }
1184 0 : if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
1185 0 : std::cerr << "Cannot use " << name
1186 0 : << " and generate code or descriptors at the same time."
1187 : << std::endl;
1188 0 : return PARSE_ARGUMENT_FAIL;
1189 : }
1190 :
1191 0 : mode_ = (name == "--encode") ? MODE_ENCODE : MODE_DECODE;
1192 :
1193 0 : if (value.empty() && name != "--decode_raw") {
1194 0 : std::cerr << "Type name for " << name << " cannot be blank." << std::endl;
1195 0 : if (name == "--decode") {
1196 0 : std::cerr << "To decode an unknown message, use --decode_raw."
1197 : << std::endl;
1198 : }
1199 : return PARSE_ARGUMENT_FAIL;
1200 0 : } else if (!value.empty() && name == "--decode_raw") {
1201 0 : std::cerr << "--decode_raw does not take a parameter." << std::endl;
1202 0 : return PARSE_ARGUMENT_FAIL;
1203 : }
1204 :
1205 0 : codec_type_ = value;
1206 :
1207 25 : } else if (name == "--error_format") {
1208 0 : if (value == "gcc") {
1209 0 : error_format_ = ERROR_FORMAT_GCC;
1210 0 : } else if (value == "msvs") {
1211 0 : error_format_ = ERROR_FORMAT_MSVS;
1212 : } else {
1213 0 : std::cerr << "Unknown error format: " << value << std::endl;
1214 0 : return PARSE_ARGUMENT_FAIL;
1215 : }
1216 :
1217 25 : } else if (name == "--plugin") {
1218 16 : if (plugin_prefix_.empty()) {
1219 0 : std::cerr << "This compiler does not support plugins." << std::endl;
1220 0 : return PARSE_ARGUMENT_FAIL;
1221 : }
1222 :
1223 : string plugin_name;
1224 : string path;
1225 :
1226 8 : string::size_type equals_pos = value.find_first_of('=');
1227 8 : if (equals_pos == string::npos) {
1228 : // Use the basename of the file.
1229 0 : string::size_type slash_pos = value.find_last_of('/');
1230 0 : if (slash_pos == string::npos) {
1231 : plugin_name = value;
1232 : } else {
1233 0 : plugin_name = value.substr(slash_pos + 1);
1234 : }
1235 : path = value;
1236 : } else {
1237 16 : plugin_name = value.substr(0, equals_pos);
1238 16 : path = value.substr(equals_pos + 1);
1239 : }
1240 :
1241 8 : plugins_[plugin_name] = path;
1242 :
1243 17 : } else if (name == "--print_free_field_numbers") {
1244 0 : if (mode_ != MODE_COMPILE) {
1245 0 : std::cerr << "Cannot use " << name
1246 0 : << " and use --encode, --decode or print "
1247 0 : << "other info at the same time." << std::endl;
1248 0 : return PARSE_ARGUMENT_FAIL;
1249 : }
1250 0 : if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
1251 0 : std::cerr << "Cannot use " << name
1252 0 : << " and generate code or descriptors at the same time."
1253 : << std::endl;
1254 0 : return PARSE_ARGUMENT_FAIL;
1255 : }
1256 0 : mode_ = MODE_PRINT;
1257 0 : print_mode_ = PRINT_FREE_FIELDS;
1258 : } else {
1259 : // Some other flag. Look it up in the generators list.
1260 : const GeneratorInfo* generator_info =
1261 34 : FindOrNull(generators_by_flag_name_, name);
1262 51 : if (generator_info == NULL &&
1263 32 : (plugin_prefix_.empty() || !HasSuffixString(name, "_out"))) {
1264 : // Check if it's a generator option flag.
1265 0 : generator_info = FindOrNull(generators_by_option_name_, name);
1266 0 : if (generator_info == NULL) {
1267 0 : std::cerr << "Unknown flag: " << name << std::endl;
1268 0 : return PARSE_ARGUMENT_FAIL;
1269 : } else {
1270 0 : string* parameters = &generator_parameters_[generator_info->flag_name];
1271 0 : if (!parameters->empty()) {
1272 0 : parameters->append(",");
1273 : }
1274 0 : parameters->append(value);
1275 : }
1276 : } else {
1277 : // It's an output flag. Add it to the output directives.
1278 17 : if (mode_ != MODE_COMPILE) {
1279 : std::cerr << "Cannot use --encode, --decode or print .proto info and "
1280 0 : "generate code at the same time." << std::endl;
1281 0 : return PARSE_ARGUMENT_FAIL;
1282 : }
1283 :
1284 : OutputDirective directive;
1285 : directive.name = name;
1286 17 : if (generator_info == NULL) {
1287 8 : directive.generator = NULL;
1288 : } else {
1289 9 : directive.generator = generator_info->generator;
1290 : }
1291 :
1292 : // Split value at ':' to separate the generator parameter from the
1293 : // filename. However, avoid doing this if the colon is part of a valid
1294 : // Windows-style absolute path.
1295 17 : string::size_type colon_pos = value.find_first_of(':');
1296 17 : if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) {
1297 : directive.output_location = value;
1298 : } else {
1299 0 : directive.parameter = value.substr(0, colon_pos);
1300 0 : directive.output_location = value.substr(colon_pos + 1);
1301 : }
1302 :
1303 17 : output_directives_.push_back(directive);
1304 : }
1305 : }
1306 :
1307 : return PARSE_ARGUMENT_DONE_AND_CONTINUE;
1308 : }
1309 :
1310 0 : void CommandLineInterface::PrintHelpText() {
1311 : // Sorry for indentation here; line wrapping would be uglier.
1312 : std::cerr <<
1313 0 : "Usage: " << executable_name_ << " [OPTION] PROTO_FILES\n"
1314 : "Parse PROTO_FILES and generate output based on the options given:\n"
1315 : " -IPATH, --proto_path=PATH Specify the directory in which to search for\n"
1316 : " imports. May be specified multiple times;\n"
1317 : " directories will be searched in order. If not\n"
1318 : " given, the current working directory is used.\n"
1319 : " --version Show version info and exit.\n"
1320 : " -h, --help Show this text and exit.\n"
1321 : " --encode=MESSAGE_TYPE Read a text-format message of the given type\n"
1322 : " from standard input and write it in binary\n"
1323 : " to standard output. The message type must\n"
1324 : " be defined in PROTO_FILES or their imports.\n"
1325 : " --decode=MESSAGE_TYPE Read a binary message of the given type from\n"
1326 : " standard input and write it in text format\n"
1327 : " to standard output. The message type must\n"
1328 : " be defined in PROTO_FILES or their imports.\n"
1329 : " --decode_raw Read an arbitrary protocol message from\n"
1330 : " standard input and write the raw tag/value\n"
1331 : " pairs in text format to standard output. No\n"
1332 : " PROTO_FILES should be given when using this\n"
1333 : " flag.\n"
1334 : " -oFILE, Writes a FileDescriptorSet (a protocol buffer,\n"
1335 : " --descriptor_set_out=FILE defined in descriptor.proto) containing all of\n"
1336 : " the input files to FILE.\n"
1337 : " --include_imports When using --descriptor_set_out, also include\n"
1338 : " all dependencies of the input files in the\n"
1339 : " set, so that the set is self-contained.\n"
1340 : " --include_source_info When using --descriptor_set_out, do not strip\n"
1341 : " SourceCodeInfo from the FileDescriptorProto.\n"
1342 : " This results in vastly larger descriptors that\n"
1343 : " include information about the original\n"
1344 : " location of each decl in the source file as\n"
1345 : " well as surrounding comments.\n"
1346 : " --dependency_out=FILE Write a dependency output file in the format\n"
1347 : " expected by make. This writes the transitive\n"
1348 : " set of input file paths to FILE\n"
1349 : " --error_format=FORMAT Set the format in which to print errors.\n"
1350 : " FORMAT may be 'gcc' (the default) or 'msvs'\n"
1351 : " (Microsoft Visual Studio format).\n"
1352 : " --print_free_field_numbers Print the free field numbers of the messages\n"
1353 : " defined in the given proto files. Groups share\n"
1354 : " the same field number space with the parent \n"
1355 : " message. Extension ranges are counted as \n"
1356 0 : " occupied fields numbers."
1357 : << std::endl;
1358 0 : if (!plugin_prefix_.empty()) {
1359 : std::cerr <<
1360 : " --plugin=EXECUTABLE Specifies a plugin executable to use.\n"
1361 : " Normally, protoc searches the PATH for\n"
1362 : " plugins, but you may specify additional\n"
1363 : " executables not in the path using this flag.\n"
1364 : " Additionally, EXECUTABLE may be of the form\n"
1365 : " NAME=PATH, in which case the given plugin name\n"
1366 : " is mapped to the given executable even if\n"
1367 0 : " the executable's own name differs." << std::endl;
1368 : }
1369 :
1370 0 : for (GeneratorMap::iterator iter = generators_by_flag_name_.begin();
1371 0 : iter != generators_by_flag_name_.end(); ++iter) {
1372 : // FIXME(kenton): If the text is long enough it will wrap, which is ugly,
1373 : // but fixing this nicely (e.g. splitting on spaces) is probably more
1374 : // trouble than it's worth.
1375 0 : std::cerr << " " << iter->first << "=OUT_DIR "
1376 0 : << string(19 - iter->first.size(), ' ') // Spaces for alignment.
1377 0 : << iter->second.help_text << std::endl;
1378 : }
1379 0 : }
1380 :
1381 17 : bool CommandLineInterface::GenerateOutput(
1382 116 : const vector<const FileDescriptor*>& parsed_files,
1383 : const OutputDirective& output_directive,
1384 : GeneratorContext* generator_context) {
1385 : // Call the generator.
1386 : string error;
1387 17 : if (output_directive.generator == NULL) {
1388 : // This is a plugin.
1389 32 : GOOGLE_CHECK(HasPrefixString(output_directive.name, "--") &&
1390 : HasSuffixString(output_directive.name, "_out"))
1391 16 : << "Bad name for plugin generator: " << output_directive.name;
1392 :
1393 : // Strip the "--" and "_out" and add the plugin prefix.
1394 16 : string plugin_name = plugin_prefix_ + "gen-" +
1395 32 : output_directive.name.substr(2, output_directive.name.size() - 6);
1396 :
1397 8 : if (!GeneratePluginOutput(parsed_files, plugin_name,
1398 : output_directive.parameter,
1399 8 : generator_context, &error)) {
1400 0 : std::cerr << output_directive.name << ": " << error << std::endl;
1401 0 : return false;
1402 : }
1403 : } else {
1404 : // Regular generator.
1405 9 : string parameters = output_directive.parameter;
1406 18 : if (!generator_parameters_[output_directive.name].empty()) {
1407 0 : if (!parameters.empty()) {
1408 0 : parameters.append(",");
1409 : }
1410 0 : parameters.append(generator_parameters_[output_directive.name]);
1411 : }
1412 9 : if (output_directive.generator->HasGenerateAll()) {
1413 0 : if (!output_directive.generator->GenerateAll(
1414 0 : parsed_files, parameters, generator_context, &error)) {
1415 : // Generator returned an error.
1416 0 : std::cerr << output_directive.name << ": "
1417 0 : << ": " << error << std::endl;
1418 : return false;
1419 : }
1420 : } else {
1421 223 : for (int i = 0; i < parsed_files.size(); i++) {
1422 98 : if (!output_directive.generator->Generate(parsed_files[i], parameters,
1423 98 : generator_context, &error)) {
1424 : // Generator returned an error.
1425 0 : std::cerr << output_directive.name << ": " << parsed_files[i]->name()
1426 0 : << ": " << error << std::endl;
1427 : return false;
1428 : }
1429 : }
1430 : }
1431 : }
1432 :
1433 : return true;
1434 : }
1435 :
1436 0 : bool CommandLineInterface::GenerateDependencyManifestFile(
1437 0 : const vector<const FileDescriptor*>& parsed_files,
1438 : const GeneratorContextMap& output_directories,
1439 : DiskSourceTree* source_tree) {
1440 0 : FileDescriptorSet file_set;
1441 :
1442 : set<const FileDescriptor*> already_seen;
1443 0 : for (int i = 0; i < parsed_files.size(); i++) {
1444 0 : GetTransitiveDependencies(parsed_files[i],
1445 : false,
1446 : &already_seen,
1447 0 : file_set.mutable_file());
1448 : }
1449 :
1450 0 : vector<string> output_filenames;
1451 0 : for (GeneratorContextMap::const_iterator iter = output_directories.begin();
1452 0 : iter != output_directories.end(); ++iter) {
1453 0 : const string& location = iter->first;
1454 0 : GeneratorContextImpl* directory = iter->second;
1455 : vector<string> relative_output_filenames;
1456 0 : directory->GetOutputFilenames(&relative_output_filenames);
1457 0 : for (int i = 0; i < relative_output_filenames.size(); i++) {
1458 0 : string output_filename = location + relative_output_filenames[i];
1459 0 : if (output_filename.compare(0, 2, "./") == 0) {
1460 0 : output_filename = output_filename.substr(2);
1461 : }
1462 0 : output_filenames.push_back(output_filename);
1463 : }
1464 0 : }
1465 :
1466 : int fd;
1467 0 : do {
1468 : fd = open(dependency_out_name_.c_str(),
1469 0 : O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
1470 0 : } while (fd < 0 && errno == EINTR);
1471 :
1472 0 : if (fd < 0) {
1473 0 : perror(dependency_out_name_.c_str());
1474 : return false;
1475 : }
1476 :
1477 0 : io::FileOutputStream out(fd);
1478 0 : io::Printer printer(&out, '$');
1479 :
1480 0 : for (int i = 0; i < output_filenames.size(); i++) {
1481 0 : printer.Print(output_filenames[i].c_str());
1482 0 : if (i == output_filenames.size() - 1) {
1483 0 : printer.Print(":");
1484 : } else {
1485 0 : printer.Print(" \\\n");
1486 : }
1487 : }
1488 :
1489 0 : for (int i = 0; i < file_set.file_size(); i++) {
1490 0 : const FileDescriptorProto& file = file_set.file(i);
1491 0 : const string& virtual_file = file.name();
1492 : string disk_file;
1493 0 : if (source_tree &&
1494 0 : source_tree->VirtualFileToDiskFile(virtual_file, &disk_file)) {
1495 0 : printer.Print(" $disk_file$", "disk_file", disk_file);
1496 0 : if (i < file_set.file_size() - 1) printer.Print("\\\n");
1497 : } else {
1498 0 : std::cerr << "Unable to identify path for file " << virtual_file
1499 : << std::endl;
1500 0 : return false;
1501 : }
1502 : }
1503 :
1504 0 : return true;
1505 : }
1506 :
1507 8 : bool CommandLineInterface::GeneratePluginOutput(
1508 40 : const vector<const FileDescriptor*>& parsed_files,
1509 : const string& plugin_name,
1510 : const string& parameter,
1511 : GeneratorContext* generator_context,
1512 : string* error) {
1513 8 : CodeGeneratorRequest request;
1514 16 : CodeGeneratorResponse response;
1515 :
1516 : // Build the request.
1517 8 : if (!parameter.empty()) {
1518 : request.set_parameter(parameter);
1519 : }
1520 :
1521 : set<const FileDescriptor*> already_seen;
1522 64 : for (int i = 0; i < parsed_files.size(); i++) {
1523 16 : request.add_file_to_generate(parsed_files[i]->name());
1524 8 : GetTransitiveDependencies(parsed_files[i],
1525 : true, // Include source code info.
1526 8 : &already_seen, request.mutable_proto_file());
1527 : }
1528 :
1529 : // Invoke the plugin.
1530 16 : Subprocess subprocess;
1531 :
1532 16 : if (plugins_.count(plugin_name) > 0) {
1533 8 : subprocess.Start(plugins_[plugin_name], Subprocess::EXACT_NAME);
1534 : } else {
1535 0 : subprocess.Start(plugin_name, Subprocess::SEARCH_PATH);
1536 : }
1537 :
1538 : string communicate_error;
1539 8 : if (!subprocess.Communicate(request, &response, &communicate_error)) {
1540 0 : *error = strings::Substitute("$0: $1", plugin_name, communicate_error);
1541 0 : return false;
1542 : }
1543 :
1544 : // Write the files. We do this even if there was a generator error in order
1545 : // to match the behavior of a compiled-in generator.
1546 : google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> current_output;
1547 40 : for (int i = 0; i < response.file_size(); i++) {
1548 16 : const CodeGeneratorResponse::File& output_file = response.file(i);
1549 :
1550 32 : if (!output_file.insertion_point().empty()) {
1551 : // Open a file for insert.
1552 : // We reset current_output to NULL first so that the old file is closed
1553 : // before the new one is opened.
1554 : current_output.reset();
1555 : current_output.reset(generator_context->OpenForInsert(
1556 0 : output_file.name(), output_file.insertion_point()));
1557 32 : } else if (!output_file.name().empty()) {
1558 : // Starting a new file. Open it.
1559 : // We reset current_output to NULL first so that the old file is closed
1560 : // before the new one is opened.
1561 : current_output.reset();
1562 32 : current_output.reset(generator_context->Open(output_file.name()));
1563 0 : } else if (current_output == NULL) {
1564 0 : *error = strings::Substitute(
1565 : "$0: First file chunk returned by plugin did not specify a file name.",
1566 : plugin_name);
1567 0 : return false;
1568 : }
1569 :
1570 : // Use CodedOutputStream for convenience; otherwise we'd need to provide
1571 : // our own buffer-copying loop.
1572 16 : io::CodedOutputStream writer(current_output.get());
1573 16 : writer.WriteString(output_file.content());
1574 16 : }
1575 :
1576 : // Check for errors.
1577 16 : if (!response.error().empty()) {
1578 : // Generator returned an error.
1579 0 : *error = response.error();
1580 : return false;
1581 : }
1582 :
1583 8 : return true;
1584 : }
1585 :
1586 0 : bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) {
1587 : // Look up the type.
1588 0 : const Descriptor* type = pool->FindMessageTypeByName(codec_type_);
1589 0 : if (type == NULL) {
1590 0 : std::cerr << "Type not defined: " << codec_type_ << std::endl;
1591 : return false;
1592 : }
1593 :
1594 0 : DynamicMessageFactory dynamic_factory(pool);
1595 0 : google::protobuf::scoped_ptr<Message> message(dynamic_factory.GetPrototype(type)->New());
1596 :
1597 : if (mode_ == MODE_ENCODE) {
1598 : SetFdToTextMode(STDIN_FILENO);
1599 : SetFdToBinaryMode(STDOUT_FILENO);
1600 : } else {
1601 : SetFdToBinaryMode(STDIN_FILENO);
1602 : SetFdToTextMode(STDOUT_FILENO);
1603 : }
1604 :
1605 0 : io::FileInputStream in(STDIN_FILENO);
1606 0 : io::FileOutputStream out(STDOUT_FILENO);
1607 :
1608 0 : if (mode_ == MODE_ENCODE) {
1609 : // Input is text.
1610 0 : ErrorPrinter error_collector(error_format_);
1611 0 : TextFormat::Parser parser;
1612 0 : parser.RecordErrorsTo(&error_collector);
1613 0 : parser.AllowPartialMessage(true);
1614 :
1615 0 : if (!parser.Parse(&in, message.get())) {
1616 0 : std::cerr << "Failed to parse input." << std::endl;
1617 0 : return false;
1618 0 : }
1619 : } else {
1620 : // Input is binary.
1621 0 : if (!message->ParsePartialFromZeroCopyStream(&in)) {
1622 0 : std::cerr << "Failed to parse input." << std::endl;
1623 : return false;
1624 : }
1625 : }
1626 :
1627 0 : if (!message->IsInitialized()) {
1628 0 : std::cerr << "warning: Input message is missing required fields: "
1629 0 : << message->InitializationErrorString() << std::endl;
1630 : }
1631 :
1632 0 : if (mode_ == MODE_ENCODE) {
1633 : // Output is binary.
1634 0 : if (!message->SerializePartialToZeroCopyStream(&out)) {
1635 0 : std::cerr << "output: I/O error." << std::endl;
1636 : return false;
1637 : }
1638 : } else {
1639 : // Output is text.
1640 0 : if (!TextFormat::Print(*message, &out)) {
1641 0 : std::cerr << "output: I/O error." << std::endl;
1642 : return false;
1643 : }
1644 : }
1645 :
1646 0 : return true;
1647 : }
1648 :
1649 0 : bool CommandLineInterface::WriteDescriptorSet(
1650 0 : const vector<const FileDescriptor*> parsed_files) {
1651 0 : FileDescriptorSet file_set;
1652 :
1653 0 : if (imports_in_descriptor_set_) {
1654 : set<const FileDescriptor*> already_seen;
1655 0 : for (int i = 0; i < parsed_files.size(); i++) {
1656 0 : GetTransitiveDependencies(parsed_files[i],
1657 : source_info_in_descriptor_set_,
1658 0 : &already_seen, file_set.mutable_file());
1659 : }
1660 : } else {
1661 : set<const FileDescriptor*> already_seen;
1662 0 : for (int i = 0; i < parsed_files.size(); i++) {
1663 0 : if (!already_seen.insert(parsed_files[i]).second) {
1664 : continue;
1665 : }
1666 0 : FileDescriptorProto* file_proto = file_set.add_file();
1667 0 : parsed_files[i]->CopyTo(file_proto);
1668 0 : if (source_info_in_descriptor_set_) {
1669 0 : parsed_files[i]->CopySourceCodeInfoTo(file_proto);
1670 : }
1671 : }
1672 : }
1673 :
1674 : int fd;
1675 0 : do {
1676 : fd = open(descriptor_set_name_.c_str(),
1677 0 : O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
1678 0 : } while (fd < 0 && errno == EINTR);
1679 :
1680 0 : if (fd < 0) {
1681 0 : perror(descriptor_set_name_.c_str());
1682 : return false;
1683 : }
1684 :
1685 0 : io::FileOutputStream out(fd);
1686 0 : if (!file_set.SerializeToZeroCopyStream(&out)) {
1687 0 : std::cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno())
1688 : << std::endl;
1689 0 : out.Close();
1690 : return false;
1691 : }
1692 0 : if (!out.Close()) {
1693 0 : std::cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno())
1694 : << std::endl;
1695 : return false;
1696 : }
1697 :
1698 0 : return true;
1699 : }
1700 :
1701 13 : void CommandLineInterface::GetTransitiveDependencies(
1702 : const FileDescriptor* file, bool include_source_code_info,
1703 : set<const FileDescriptor*>* already_seen,
1704 : RepeatedPtrField<FileDescriptorProto>* output) {
1705 13 : if (!already_seen->insert(file).second) {
1706 : // Already saw this file. Skip.
1707 13 : return;
1708 : }
1709 :
1710 : // Add all dependencies.
1711 23 : for (int i = 0; i < file->dependency_count(); i++) {
1712 : GetTransitiveDependencies(file->dependency(i), include_source_code_info,
1713 28 : already_seen, output);
1714 : }
1715 :
1716 : // Add this file.
1717 13 : FileDescriptorProto* new_descriptor = output->Add();
1718 13 : file->CopyTo(new_descriptor);
1719 13 : if (include_source_code_info) {
1720 13 : file->CopySourceCodeInfoTo(new_descriptor);
1721 : }
1722 : }
1723 :
1724 : namespace {
1725 :
1726 : // Utility function for PrintFreeFieldNumbers.
1727 : // Stores occupied ranges into the ranges parameter, and next level of sub
1728 : // message types into the nested_messages parameter. The FieldRange is left
1729 : // inclusive, right exclusive. i.e. [a, b).
1730 : //
1731 : // Nested Messages:
1732 : // Note that it only stores the nested message type, iff the nested type is
1733 : // either a direct child of the given descriptor, or the nested type is a
1734 : // decendent of the given descriptor and all the nodes between the
1735 : // nested type and the given descriptor are group types. e.g.
1736 : //
1737 : // message Foo {
1738 : // message Bar {
1739 : // message NestedBar {}
1740 : // }
1741 : // group Baz = 1 {
1742 : // group NestedBazGroup = 2 {
1743 : // message Quz {
1744 : // message NestedQuz {}
1745 : // }
1746 : // }
1747 : // message NestedBaz {}
1748 : // }
1749 : // }
1750 : //
1751 : // In this case, Bar, Quz and NestedBaz will be added into the nested types.
1752 : // Since free field numbers of group types will not be printed, this makes sure
1753 : // the nested message types in groups will not be dropped. The nested_messages
1754 : // parameter will contain the direct children (when groups are ignored in the
1755 : // tree) of the given descriptor for the caller to traverse. The declaration
1756 : // order of the nested messages is also preserved.
1757 : typedef pair<int, int> FieldRange;
1758 0 : void GatherOccupiedFieldRanges(const Descriptor* descriptor,
1759 : set<FieldRange>* ranges,
1760 : vector<const Descriptor*>* nested_messages) {
1761 : set<const Descriptor*> groups;
1762 0 : for (int i = 0; i < descriptor->field_count(); ++i) {
1763 0 : const FieldDescriptor* fd = descriptor->field(i);
1764 0 : ranges->insert(FieldRange(fd->number(), fd->number() + 1));
1765 0 : if (fd->type() == FieldDescriptor::TYPE_GROUP) {
1766 0 : groups.insert(fd->message_type());
1767 : }
1768 : }
1769 0 : for (int i = 0; i < descriptor->extension_range_count(); ++i) {
1770 0 : ranges->insert(FieldRange(descriptor->extension_range(i)->start,
1771 0 : descriptor->extension_range(i)->end));
1772 : }
1773 0 : for (int i = 0; i < descriptor->reserved_range_count(); ++i) {
1774 0 : ranges->insert(FieldRange(descriptor->reserved_range(i)->start,
1775 0 : descriptor->reserved_range(i)->end));
1776 : }
1777 : // Handle the nested messages/groups in declaration order to make it
1778 : // post-order strict.
1779 0 : for (int i = 0; i < descriptor->nested_type_count(); ++i) {
1780 0 : const Descriptor* nested_desc = descriptor->nested_type(i);
1781 0 : if (groups.find(nested_desc) != groups.end()) {
1782 0 : GatherOccupiedFieldRanges(nested_desc, ranges, nested_messages);
1783 : } else {
1784 0 : nested_messages->push_back(nested_desc);
1785 : }
1786 : }
1787 0 : }
1788 :
1789 : // Utility function for PrintFreeFieldNumbers.
1790 : // Actually prints the formatted free field numbers for given message name and
1791 : // occupied ranges.
1792 0 : void FormatFreeFieldNumbers(const string& name,
1793 : const set<FieldRange>& ranges) {
1794 : string output;
1795 0 : StringAppendF(&output, "%-35s free:", name.c_str());
1796 0 : int next_free_number = 1;
1797 0 : for (set<FieldRange>::const_iterator i = ranges.begin();
1798 0 : i != ranges.end(); ++i) {
1799 : // This happens when groups re-use parent field numbers, in which
1800 : // case we skip the FieldRange entirely.
1801 0 : if (next_free_number >= i->second) continue;
1802 :
1803 0 : if (next_free_number < i->first) {
1804 0 : if (next_free_number + 1 == i->first) {
1805 : // Singleton
1806 0 : StringAppendF(&output, " %d", next_free_number);
1807 : } else {
1808 : // Range
1809 0 : StringAppendF(&output, " %d-%d", next_free_number, i->first - 1);
1810 : }
1811 : }
1812 0 : next_free_number = i->second;
1813 : }
1814 0 : if (next_free_number <= FieldDescriptor::kMaxNumber) {
1815 0 : StringAppendF(&output, " %d-INF", next_free_number);
1816 : }
1817 0 : std::cout << output << std::endl;
1818 0 : }
1819 :
1820 : } // namespace
1821 :
1822 0 : void CommandLineInterface::PrintFreeFieldNumbers(
1823 0 : const Descriptor* descriptor) {
1824 : set<FieldRange> ranges;
1825 : vector<const Descriptor*> nested_messages;
1826 0 : GatherOccupiedFieldRanges(descriptor, &ranges, &nested_messages);
1827 :
1828 0 : for (int i = 0; i < nested_messages.size(); ++i) {
1829 0 : PrintFreeFieldNumbers(nested_messages[i]);
1830 : }
1831 0 : FormatFreeFieldNumbers(descriptor->full_name(), ranges);
1832 0 : }
1833 :
1834 :
1835 :
1836 : } // namespace compiler
1837 : } // namespace protobuf
1838 51 : } // namespace google
|