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