1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// https://developers.google.com/protocol-buffers/ 4// 5// Redistribution and use in source and binary forms, with or without 6// modification, are permitted provided that the following conditions are 7// met: 8// 9// * Redistributions of source code must retain the above copyright 10// notice, this list of conditions and the following disclaimer. 11// * Redistributions in binary form must reproduce the above 12// copyright notice, this list of conditions and the following disclaimer 13// in the documentation and/or other materials provided with the 14// distribution. 15// * Neither the name of Google Inc. nor the names of its 16// contributors may be used to endorse or promote products derived from 17// this software without specific prior written permission. 18// 19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31// Author: kenton@google.com (Kenton Varda) 32// Based on original Protocol Buffers design by 33// Sanjay Ghemawat, Jeff Dean, and others. 34 35#include <google/protobuf/compiler/command_line_interface.h> 36 37#include <stdio.h> 38#include <sys/types.h> 39#include <sys/stat.h> 40#include <fcntl.h> 41#ifdef _MSC_VER 42#include <io.h> 43#include <direct.h> 44#else 45#include <unistd.h> 46#endif 47#include <errno.h> 48#include <iostream> 49#include <ctype.h> 50 51#include <google/protobuf/stubs/hash.h> 52#include <memory> 53 54#include <google/protobuf/stubs/common.h> 55#include <google/protobuf/stubs/stringprintf.h> 56#include <google/protobuf/compiler/importer.h> 57#include <google/protobuf/compiler/code_generator.h> 58#include <google/protobuf/compiler/plugin.pb.h> 59#include <google/protobuf/compiler/subprocess.h> 60#include <google/protobuf/compiler/zip_writer.h> 61#include <google/protobuf/descriptor.h> 62#include <google/protobuf/text_format.h> 63#include <google/protobuf/dynamic_message.h> 64#include <google/protobuf/io/coded_stream.h> 65#include <google/protobuf/io/zero_copy_stream_impl.h> 66#include <google/protobuf/io/printer.h> 67#include <google/protobuf/stubs/strutil.h> 68#include <google/protobuf/stubs/substitute.h> 69#include <google/protobuf/stubs/map_util.h> 70#include <google/protobuf/stubs/stl_util.h> 71 72 73namespace google { 74namespace protobuf { 75namespace 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 101namespace { 102#if defined(_WIN32) && !defined(__CYGWIN__) 103static const char* kPathSeparator = ";"; 104#else 105static 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? 111static 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 122void 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 132void 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 142void AddTrailingSlash(string* path) { 143 if (!path->empty() && path->at(path->size() - 1) != '/') { 144 path->push_back('/'); 145 } 146} 147 148bool 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|. 163bool 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. 185class 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. 237class 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 273class 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 305CommandLineInterface::GeneratorContextImpl::GeneratorContextImpl( 306 const vector<const FileDescriptor*>& parsed_files) 307 : parsed_files_(parsed_files), 308 had_error_(false) { 309} 310 311CommandLineInterface::GeneratorContextImpl::~GeneratorContextImpl() { 312 STLDeleteValues(&files_); 313} 314 315bool 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 390bool 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 431void 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 441io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open( 442 const string& filename) { 443 return new MemoryOutputStream(this, filename, false); 444} 445 446io::ZeroCopyOutputStream* 447CommandLineInterface::GeneratorContextImpl::OpenForAppend( 448 const string& filename) { 449 return new MemoryOutputStream(this, filename, true); 450} 451 452io::ZeroCopyOutputStream* 453CommandLineInterface::GeneratorContextImpl::OpenForInsert( 454 const string& filename, const string& insertion_point) { 455 return new MemoryOutputStream(this, filename, insertion_point); 456} 457 458// ------------------------------------------------------------------- 459 460CommandLineInterface::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 468CommandLineInterface::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 477CommandLineInterface::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 583CommandLineInterface::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) {} 591CommandLineInterface::~CommandLineInterface() {} 592 593void 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 603void 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 616void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) { 617 plugin_prefix_ = exe_name_prefix; 618} 619 620int 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 766void 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 783bool 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 822CommandLineInterface::ParseArgumentStatus 823CommandLineInterface::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 883bool 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 944CommandLineInterface::ParseArgumentStatus 945CommandLineInterface::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 1170void 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 1237bool 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 1282bool 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 1361bool 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 1424bool 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 1470void 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 1493namespace { 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. 1526typedef pair<int, int> FieldRange; 1527void 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. 1557void 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 1587void 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