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