PageRenderTime 171ms CodeModel.GetById 50ms app.highlight 110ms RepoModel.GetById 1ms app.codeStats 1ms

/thirdparty/breakpad/third_party/protobuf/protobuf/src/google/protobuf/compiler/command_line_interface.cc

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