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