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