Index: third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc |
diff --git a/third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc b/third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc |
index b9293c97de1238bb212aedf6e9aa756b62624da9..deb3d0f19a12d5eb87a2ad726177291611febeac 100644 |
--- a/third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc |
+++ b/third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc |
@@ -1,6 +1,6 @@ |
// Protocol Buffers - Google's data interchange format |
// Copyright 2008 Google Inc. All rights reserved. |
-// http://code.google.com/p/protobuf/ |
+// https://developers.google.com/protocol-buffers/ |
// |
// Redistribution and use in source and binary forms, with or without |
// modification, are permitted provided that the following conditions are |
@@ -48,9 +48,17 @@ |
#include <iostream> |
#include <ctype.h> |
-#include <google/protobuf/stubs/hash.h> |
+#include <memory> |
+#ifndef _SHARED_PTR_H |
+#include <google/protobuf/stubs/shared_ptr.h> |
+#endif |
+ |
+#ifdef __APPLE__ |
+#include <mach-o/dyld.h> |
+#endif |
#include <google/protobuf/stubs/common.h> |
+#include <google/protobuf/stubs/stringprintf.h> |
#include <google/protobuf/compiler/importer.h> |
#include <google/protobuf/compiler/code_generator.h> |
#include <google/protobuf/compiler/plugin.pb.h> |
@@ -62,9 +70,10 @@ |
#include <google/protobuf/io/coded_stream.h> |
#include <google/protobuf/io/zero_copy_stream_impl.h> |
#include <google/protobuf/io/printer.h> |
+#include <google/protobuf/stubs/logging.h> |
#include <google/protobuf/stubs/strutil.h> |
#include <google/protobuf/stubs/substitute.h> |
-#include <google/protobuf/stubs/map-util.h> |
+#include <google/protobuf/stubs/map_util.h> |
#include <google/protobuf/stubs/stl_util.h> |
@@ -147,7 +156,7 @@ bool VerifyDirectoryExists(const string& path) { |
if (path.empty()) return true; |
if (access(path.c_str(), F_OK) == -1) { |
- cerr << path << ": " << strerror(errno) << endl; |
+ std::cerr << path << ": " << strerror(errno) << std::endl; |
return false; |
} else { |
return true; |
@@ -160,15 +169,14 @@ bool VerifyDirectoryExists(const string& path) { |
// directories listed in |filename|. |
bool TryCreateParentDirectory(const string& prefix, const string& filename) { |
// Recursively create parent directories to the output file. |
- vector<string> parts; |
- SplitStringUsing(filename, "/", &parts); |
+ vector<string> parts = Split(filename, "/", true); |
string path_so_far = prefix; |
for (int i = 0; i < parts.size() - 1; i++) { |
path_so_far += parts[i]; |
if (mkdir(path_so_far.c_str(), 0777) != 0) { |
if (errno != EEXIST) { |
- cerr << filename << ": while trying to create directory " |
- << path_so_far << ": " << strerror(errno) << endl; |
+ std::cerr << filename << ": while trying to create directory " |
+ << path_so_far << ": " << strerror(errno) << std::endl; |
return false; |
} |
} |
@@ -178,6 +186,78 @@ bool TryCreateParentDirectory(const string& prefix, const string& filename) { |
return true; |
} |
+// Get the absolute path of this protoc binary. |
+bool GetProtocAbsolutePath(string* path) { |
+#ifdef _WIN32 |
+ char buffer[MAX_PATH]; |
+ int len = GetModuleFileNameA(NULL, buffer, MAX_PATH); |
+#elif __APPLE__ |
+ char buffer[PATH_MAX]; |
+ int len = 0; |
+ |
+ char dirtybuffer[PATH_MAX]; |
+ uint32_t size = sizeof(dirtybuffer); |
+ if (_NSGetExecutablePath(dirtybuffer, &size) == 0) { |
+ realpath(dirtybuffer, buffer); |
+ len = strlen(buffer); |
+ } |
+#else |
+ char buffer[PATH_MAX]; |
+ int len = readlink("/proc/self/exe", buffer, PATH_MAX); |
+#endif |
+ if (len > 0) { |
+ path->assign(buffer, len); |
+ return true; |
+ } else { |
+ return false; |
+ } |
+} |
+ |
+// Whether a path is where google/protobuf/descriptor.proto and other well-known |
+// type protos are installed. |
+bool IsInstalledProtoPath(const string& path) { |
+ // Checking the descriptor.proto file should be good enough. |
+ string file_path = path + "/google/protobuf/descriptor.proto"; |
+ return access(file_path.c_str(), F_OK) != -1; |
+} |
+ |
+// Add the paths where google/protobuf/descritor.proto and other well-known |
+// type protos are installed. |
+void AddDefaultProtoPaths(vector<pair<string, string> >* paths) { |
+ // TODO(xiaofeng): The code currently only checks relative paths of where |
+ // the protoc binary is installed. We probably should make it handle more |
+ // cases than that. |
+ string path; |
+ if (!GetProtocAbsolutePath(&path)) { |
+ return; |
+ } |
+ // Strip the binary name. |
+ size_t pos = path.find_last_of("/\\"); |
+ if (pos == string::npos || pos == 0) { |
+ return; |
+ } |
+ path = path.substr(0, pos); |
+ // Check the binary's directory. |
+ if (IsInstalledProtoPath(path)) { |
+ paths->push_back(pair<string, string>("", path)); |
+ return; |
+ } |
+ // Check if there is an include subdirectory. |
+ if (IsInstalledProtoPath(path + "/include")) { |
+ paths->push_back(pair<string, string>("", path + "/include")); |
+ return; |
+ } |
+ // Check if the upper level directory has an "include" subdirectory. |
+ pos = path.find_last_of("/\\"); |
+ if (pos == string::npos || pos == 0) { |
+ return; |
+ } |
+ path = path.substr(0, pos); |
+ if (IsInstalledProtoPath(path + "/include")) { |
+ paths->push_back(pair<string, string>("", path + "/include")); |
+ return; |
+ } |
+} |
} // namespace |
// A MultiFileErrorCollector that prints errors to stderr. |
@@ -191,15 +271,35 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, |
// implements MultiFileErrorCollector ------------------------------ |
void AddError(const string& filename, int line, int column, |
const string& message) { |
+ AddErrorOrWarning(filename, line, column, message, "error", std::cerr); |
+ } |
+ |
+ void AddWarning(const string& filename, int line, int column, |
+ const string& message) { |
+ AddErrorOrWarning(filename, line, column, message, "warning", std::clog); |
+ } |
+ // implements io::ErrorCollector ----------------------------------- |
+ void AddError(int line, int column, const string& message) { |
+ AddError("input", line, column, message); |
+ } |
+ |
+ void AddWarning(int line, int column, const string& message) { |
+ AddErrorOrWarning("input", line, column, message, "warning", std::clog); |
+ } |
+ |
+ private: |
+ void AddErrorOrWarning( |
+ const string& filename, int line, int column, |
+ const string& message, const string& type, ostream& out) { |
// Print full path when running under MSVS |
string dfile; |
if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS && |
tree_ != NULL && |
tree_->VirtualFileToDiskFile(filename, &dfile)) { |
- cerr << dfile; |
+ out << dfile; |
} else { |
- cerr << filename; |
+ out << filename; |
} |
// Users typically expect 1-based line/column numbers, so we add 1 |
@@ -208,23 +308,22 @@ class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, |
// Allow for both GCC- and Visual-Studio-compatible output. |
switch (format_) { |
case CommandLineInterface::ERROR_FORMAT_GCC: |
- cerr << ":" << (line + 1) << ":" << (column + 1); |
+ out << ":" << (line + 1) << ":" << (column + 1); |
break; |
case CommandLineInterface::ERROR_FORMAT_MSVS: |
- cerr << "(" << (line + 1) << ") : error in column=" << (column + 1); |
+ out << "(" << (line + 1) << ") : " |
+ << type << " in column=" << (column + 1); |
break; |
} |
} |
- cerr << ": " << message << endl; |
- } |
- |
- // implements io::ErrorCollector ----------------------------------- |
- void AddError(int line, int column, const string& message) { |
- AddError("input", line, column, message); |
+ if (type == "warning") { |
+ out << ": warning: " << message << std::endl; |
+ } else { |
+ out << ": " << message << std::endl; |
+ } |
} |
- private: |
const ErrorFormat format_; |
DiskSourceTree *tree_; |
}; |
@@ -250,8 +349,12 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext { |
// format, unless one has already been written. |
void AddJarManifest(); |
+ // Get name of all output files. |
+ void GetOutputFilenames(vector<string>* output_filenames); |
+ |
// implements GeneratorContext -------------------------------------- |
io::ZeroCopyOutputStream* Open(const string& filename); |
+ io::ZeroCopyOutputStream* OpenForAppend(const string& filename); |
io::ZeroCopyOutputStream* OpenForInsert( |
const string& filename, const string& insertion_point); |
void ListParsedFiles(vector<const FileDescriptor*>* output) { |
@@ -271,7 +374,8 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext { |
class CommandLineInterface::MemoryOutputStream |
: public io::ZeroCopyOutputStream { |
public: |
- MemoryOutputStream(GeneratorContextImpl* directory, const string& filename); |
+ MemoryOutputStream(GeneratorContextImpl* directory, const string& filename, |
+ bool append_mode); |
MemoryOutputStream(GeneratorContextImpl* directory, const string& filename, |
const string& insertion_point); |
virtual ~MemoryOutputStream(); |
@@ -290,8 +394,11 @@ class CommandLineInterface::MemoryOutputStream |
// The string we're building. |
string data_; |
+ // Whether we should append the output stream to the existing file. |
+ bool append_mode_; |
+ |
// StringOutputStream writing to data_. |
- scoped_ptr<io::StringOutputStream> inner_; |
+ google::protobuf::scoped_ptr<io::StringOutputStream> inner_; |
}; |
// ------------------------------------------------------------------- |
@@ -336,7 +443,7 @@ bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk( |
if (file_descriptor < 0) { |
int error = errno; |
- cerr << filename << ": " << strerror(error); |
+ std::cerr << filename << ": " << strerror(error); |
return false; |
} |
@@ -360,9 +467,9 @@ bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk( |
if (write_result < 0) { |
int error = errno; |
- cerr << filename << ": write: " << strerror(error); |
+ std::cerr << filename << ": write: " << strerror(error); |
} else { |
- cerr << filename << ": write() returned zero?" << endl; |
+ std::cerr << filename << ": write() returned zero?" << std::endl; |
} |
return false; |
} |
@@ -373,7 +480,7 @@ bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk( |
if (close(file_descriptor) != 0) { |
int error = errno; |
- cerr << filename << ": close: " << strerror(error); |
+ std::cerr << filename << ": close: " << strerror(error); |
return false; |
} |
} |
@@ -396,7 +503,7 @@ bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip( |
if (file_descriptor < 0) { |
int error = errno; |
- cerr << filename << ": " << strerror(error); |
+ std::cerr << filename << ": " << strerror(error); |
return false; |
} |
@@ -412,11 +519,11 @@ bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip( |
zip_writer.WriteDirectory(); |
if (stream.GetErrno() != 0) { |
- cerr << filename << ": " << strerror(stream.GetErrno()) << endl; |
+ std::cerr << filename << ": " << strerror(stream.GetErrno()) << std::endl; |
} |
if (!stream.Close()) { |
- cerr << filename << ": " << strerror(stream.GetErrno()) << endl; |
+ std::cerr << filename << ": " << strerror(stream.GetErrno()) << std::endl; |
} |
return true; |
@@ -432,9 +539,23 @@ void CommandLineInterface::GeneratorContextImpl::AddJarManifest() { |
} |
} |
+void CommandLineInterface::GeneratorContextImpl::GetOutputFilenames( |
+ vector<string>* output_filenames) { |
+ for (map<string, string*>::iterator iter = files_.begin(); |
+ iter != files_.end(); ++iter) { |
+ output_filenames->push_back(iter->first); |
+ } |
+} |
+ |
io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open( |
const string& filename) { |
- return new MemoryOutputStream(this, filename); |
+ return new MemoryOutputStream(this, filename, false); |
+} |
+ |
+io::ZeroCopyOutputStream* |
+CommandLineInterface::GeneratorContextImpl::OpenForAppend( |
+ const string& filename) { |
+ return new MemoryOutputStream(this, filename, true); |
} |
io::ZeroCopyOutputStream* |
@@ -446,9 +567,10 @@ CommandLineInterface::GeneratorContextImpl::OpenForInsert( |
// ------------------------------------------------------------------- |
CommandLineInterface::MemoryOutputStream::MemoryOutputStream( |
- GeneratorContextImpl* directory, const string& filename) |
+ GeneratorContextImpl* directory, const string& filename, bool append_mode) |
: directory_(directory), |
filename_(filename), |
+ append_mode_(append_mode), |
inner_(new io::StringOutputStream(&data_)) { |
} |
@@ -471,8 +593,13 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() { |
if (insertion_point_.empty()) { |
// This was just a regular Open(). |
if (*map_slot != NULL) { |
- cerr << filename_ << ": Tried to write the same file twice." << endl; |
- directory_->had_error_ = true; |
+ if (append_mode_) { |
+ (*map_slot)->append(data_); |
+ } else { |
+ std::cerr << filename_ << ": Tried to write the same file twice." |
+ << std::endl; |
+ directory_->had_error_ = true; |
+ } |
return; |
} |
@@ -488,8 +615,9 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() { |
// Find the file we are going to insert into. |
if (*map_slot == NULL) { |
- cerr << filename_ << ": Tried to insert into file that doesn't exist." |
- << endl; |
+ std::cerr << filename_ |
+ << ": Tried to insert into file that doesn't exist." |
+ << std::endl; |
directory_->had_error_ = true; |
return; |
} |
@@ -501,8 +629,8 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() { |
string::size_type pos = target->find(magic_string); |
if (pos == string::npos) { |
- cerr << filename_ << ": insertion point \"" << insertion_point_ |
- << "\" not found." << endl; |
+ std::cerr << filename_ << ": insertion point \"" << insertion_point_ |
+ << "\" not found." << std::endl; |
directory_->had_error_ = true; |
return; |
} |
@@ -565,6 +693,7 @@ CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() { |
CommandLineInterface::CommandLineInterface() |
: mode_(MODE_COMPILE), |
+ print_mode_(PRINT_NONE), |
error_format_(ERROR_FORMAT_GCC), |
imports_in_descriptor_set_(false), |
source_info_in_descriptor_set_(false), |
@@ -610,6 +739,8 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { |
break; |
} |
+ AddDefaultProtoPaths(&proto_path_); |
+ |
// Set up the source tree. |
DiskSourceTree source_tree; |
for (int i = 0; i < proto_path_.size(); i++) { |
@@ -632,7 +763,9 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { |
// Parse each file. |
for (int i = 0; i < input_files_.size(); i++) { |
// Import the file. |
+ importer.AddUnusedImportTrackFile(input_files_[i]); |
const FileDescriptor* parsed_file = importer.Import(input_files_[i]); |
+ importer.ClearUnusedImportTrackFiles(); |
if (parsed_file == NULL) return 1; |
parsed_files.push_back(parsed_file); |
@@ -647,7 +780,6 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { |
// We construct a separate GeneratorContext for each output location. Note |
// that two code generators may output to the same location, in which case |
// they should share a single GeneratorContext so that OpenForInsert() works. |
- typedef hash_map<string, GeneratorContextImpl*> GeneratorContextMap; |
GeneratorContextMap output_directories; |
// Generate output. |
@@ -694,6 +826,13 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { |
} |
} |
+ if (!dependency_out_name_.empty()) { |
+ if (!GenerateDependencyManifestFile(parsed_files, output_directories, |
+ &source_tree)) { |
+ return 1; |
+ } |
+ } |
+ |
STLDeleteValues(&output_directories); |
if (!descriptor_set_name_.empty()) { |
@@ -721,6 +860,25 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) { |
} |
} |
+ if (mode_ == MODE_PRINT) { |
+ switch (print_mode_) { |
+ case PRINT_FREE_FIELDS: |
+ for (int i = 0; i < parsed_files.size(); ++i) { |
+ const FileDescriptor* fd = parsed_files[i]; |
+ for (int j = 0; j < fd->message_type_count(); ++j) { |
+ PrintFreeFieldNumbers(fd->message_type(j)); |
+ } |
+ } |
+ break; |
+ case PRINT_NONE: |
+ GOOGLE_LOG(ERROR) << "If the code reaches here, it usually means a bug of " |
+ "flag parsing in the CommonadLineInterface."; |
+ return 1; |
+ |
+ // Do not add a default case. |
+ } |
+ } |
+ |
return 0; |
} |
@@ -733,8 +891,10 @@ void CommandLineInterface::Clear() { |
output_directives_.clear(); |
codec_type_.clear(); |
descriptor_set_name_.clear(); |
+ dependency_out_name_.clear(); |
mode_ = MODE_COMPILE; |
+ print_mode_ = PRINT_NONE; |
imports_in_descriptor_set_ = false; |
source_info_in_descriptor_set_ = false; |
disallow_services_ = false; |
@@ -750,27 +910,31 @@ bool CommandLineInterface::MakeInputsBeProtoPathRelative( |
input_files_[i] = virtual_file; |
break; |
case DiskSourceTree::SHADOWED: |
- cerr << input_files_[i] << ": Input is shadowed in the --proto_path " |
- "by \"" << shadowing_disk_file << "\". Either use the latter " |
- "file as your input or reorder the --proto_path so that the " |
- "former file's location comes first." << endl; |
+ std::cerr << input_files_[i] |
+ << ": Input is shadowed in the --proto_path by \"" |
+ << shadowing_disk_file |
+ << "\". Either use the latter file as your input or reorder " |
+ "the --proto_path so that the former file's location " |
+ "comes first." << std::endl; |
return false; |
case DiskSourceTree::CANNOT_OPEN: |
- cerr << input_files_[i] << ": " << strerror(errno) << endl; |
+ std::cerr << input_files_[i] << ": " << strerror(errno) << std::endl; |
return false; |
case DiskSourceTree::NO_MAPPING: |
// First check if the file exists at all. |
if (access(input_files_[i].c_str(), F_OK) < 0) { |
// File does not even exist. |
- cerr << input_files_[i] << ": " << strerror(ENOENT) << endl; |
+ std::cerr << input_files_[i] << ": " << strerror(ENOENT) << std::endl; |
} else { |
- cerr << input_files_[i] << ": File does not reside within any path " |
- "specified using --proto_path (or -I). You must specify a " |
- "--proto_path which encompasses this file. Note that the " |
- "proto_path must be an exact prefix of the .proto file " |
- "names -- protoc is too dumb to figure out when two paths " |
- "(e.g. absolute and relative) are equivalent (it's harder " |
- "than you think)." << endl; |
+ std::cerr |
+ << input_files_[i] |
+ << ": File does not reside within any path " |
+ "specified using --proto_path (or -I). You must specify a " |
+ "--proto_path which encompasses this file. Note that the " |
+ "proto_path must be an exact prefix of the .proto file " |
+ "names -- protoc is too dumb to figure out when two paths " |
+ "(e.g. absolute and relative) are equivalent (it's harder " |
+ "than you think)." << std::endl; |
} |
return false; |
} |
@@ -790,9 +954,10 @@ CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { |
if (ParseArgument(argv[i], &name, &value)) { |
// Returned true => Use the next argument as the flag value. |
if (i + 1 == argc || argv[i+1][0] == '-') { |
- cerr << "Missing value for flag: " << name << endl; |
+ std::cerr << "Missing value for flag: " << name << std::endl; |
if (name == "--decode") { |
- cerr << "To decode an unknown message, use --decode_raw." << endl; |
+ std::cerr << "To decode an unknown message, use --decode_raw." |
+ << std::endl; |
} |
return PARSE_ARGUMENT_FAIL; |
} else { |
@@ -817,24 +982,36 @@ CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { |
// Check some errror cases. |
bool decoding_raw = (mode_ == MODE_DECODE) && codec_type_.empty(); |
if (decoding_raw && !input_files_.empty()) { |
- cerr << "When using --decode_raw, no input files should be given." << endl; |
+ std::cerr << "When using --decode_raw, no input files should be given." |
+ << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} else if (!decoding_raw && input_files_.empty()) { |
- cerr << "Missing input file." << endl; |
+ std::cerr << "Missing input file." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
if (mode_ == MODE_COMPILE && output_directives_.empty() && |
descriptor_set_name_.empty()) { |
- cerr << "Missing output directives." << endl; |
+ std::cerr << "Missing output directives." << std::endl; |
+ return PARSE_ARGUMENT_FAIL; |
+ } |
+ if (mode_ != MODE_COMPILE && !dependency_out_name_.empty()) { |
+ std::cerr << "Can only use --dependency_out=FILE when generating code." |
+ << std::endl; |
+ return PARSE_ARGUMENT_FAIL; |
+ } |
+ if (!dependency_out_name_.empty() && input_files_.size() > 1) { |
+ std::cerr |
+ << "Can only process one input file when using --dependency_out=FILE." |
+ << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) { |
- cerr << "--include_imports only makes sense when combined with " |
- "--descriptor_set_out." << endl; |
+ std::cerr << "--include_imports only makes sense when combined with " |
+ "--descriptor_set_out." << std::endl; |
} |
if (source_info_in_descriptor_set_ && descriptor_set_name_.empty()) { |
- cerr << "--include_source_info only makes sense when combined with " |
- "--descriptor_set_out." << endl; |
+ std::cerr << "--include_source_info only makes sense when combined with " |
+ "--descriptor_set_out." << std::endl; |
} |
return PARSE_ARGUMENT_DONE_AND_CONTINUE; |
@@ -889,7 +1066,8 @@ bool CommandLineInterface::ParseArgument(const char* arg, |
*name == "--include_imports" || |
*name == "--include_source_info" || |
*name == "--version" || |
- *name == "--decode_raw") { |
+ *name == "--decode_raw" || |
+ *name == "--print_free_field_numbers") { |
// HACK: These are the only flags that don't take a value. |
// They probably should not be hard-coded like this but for now it's |
// not worth doing better. |
@@ -906,10 +1084,12 @@ CommandLineInterface::InterpretArgument(const string& name, |
if (name.empty()) { |
// Not a flag. Just a filename. |
if (value.empty()) { |
- cerr << "You seem to have passed an empty string as one of the " |
- "arguments to " << executable_name_ << ". This is actually " |
- "sort of hard to do. Congrats. Unfortunately it is not valid " |
- "input so the program is going to die now." << endl; |
+ std::cerr |
+ << "You seem to have passed an empty string as one of the " |
+ "arguments to " << executable_name_ |
+ << ". This is actually " |
+ "sort of hard to do. Congrats. Unfortunately it is not valid " |
+ "input so the program is going to die now." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
@@ -919,8 +1099,8 @@ CommandLineInterface::InterpretArgument(const string& name, |
// Java's -classpath (and some other languages) delimits path components |
// with colons. Let's accept that syntax too just to make things more |
// intuitive. |
- vector<string> parts; |
- SplitStringUsing(value, kPathSeparator, &parts); |
+ vector<string> parts = Split( |
+ value, kPathSeparator, true); |
for (int i = 0; i < parts.size(); i++) { |
string virtual_path; |
@@ -936,14 +1116,16 @@ CommandLineInterface::InterpretArgument(const string& name, |
} |
if (disk_path.empty()) { |
- cerr << "--proto_path passed empty directory name. (Use \".\" for " |
- "current directory.)" << endl; |
+ std::cerr |
+ << "--proto_path passed empty directory name. (Use \".\" for " |
+ "current directory.)" << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
// Make sure disk path exists, warn otherwise. |
if (access(disk_path.c_str(), F_OK) < 0) { |
- cerr << disk_path << ": warning: directory does not exist." << endl; |
+ std::cerr << disk_path << ": warning: directory does not exist." |
+ << std::endl; |
} |
// Don't use make_pair as the old/default standard library on Solaris |
@@ -954,30 +1136,42 @@ CommandLineInterface::InterpretArgument(const string& name, |
} else if (name == "-o" || name == "--descriptor_set_out") { |
if (!descriptor_set_name_.empty()) { |
- cerr << name << " may only be passed once." << endl; |
+ std::cerr << name << " may only be passed once." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
if (value.empty()) { |
- cerr << name << " requires a non-empty value." << endl; |
+ std::cerr << name << " requires a non-empty value." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
if (mode_ != MODE_COMPILE) { |
- cerr << "Cannot use --encode or --decode and generate descriptors at the " |
- "same time." << endl; |
+ std::cerr |
+ << "Cannot use --encode or --decode and generate descriptors at the " |
+ "same time." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
descriptor_set_name_ = value; |
+ } else if (name == "--dependency_out") { |
+ if (!dependency_out_name_.empty()) { |
+ std::cerr << name << " may only be passed once." << std::endl; |
+ return PARSE_ARGUMENT_FAIL; |
+ } |
+ if (value.empty()) { |
+ std::cerr << name << " requires a non-empty value." << std::endl; |
+ return PARSE_ARGUMENT_FAIL; |
+ } |
+ dependency_out_name_ = value; |
+ |
} else if (name == "--include_imports") { |
if (imports_in_descriptor_set_) { |
- cerr << name << " may only be passed once." << endl; |
+ std::cerr << name << " may only be passed once." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
imports_in_descriptor_set_ = true; |
} else if (name == "--include_source_info") { |
if (source_info_in_descriptor_set_) { |
- cerr << name << " may only be passed once." << endl; |
+ std::cerr << name << " may only be passed once." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
source_info_in_descriptor_set_ = true; |
@@ -988,7 +1182,7 @@ CommandLineInterface::InterpretArgument(const string& name, |
} else if (name == "--version") { |
if (!version_info_.empty()) { |
- cout << version_info_ << endl; |
+ std::cout << version_info_ << std::endl; |
} |
cout << "libprotoc " |
<< protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION) |
@@ -1001,25 +1195,28 @@ CommandLineInterface::InterpretArgument(const string& name, |
} else if (name == "--encode" || name == "--decode" || |
name == "--decode_raw") { |
if (mode_ != MODE_COMPILE) { |
- cerr << "Only one of --encode and --decode can be specified." << endl; |
+ std::cerr << "Only one of --encode and --decode can be specified." |
+ << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
if (!output_directives_.empty() || !descriptor_set_name_.empty()) { |
- cerr << "Cannot use " << name |
- << " and generate code or descriptors at the same time." << endl; |
+ std::cerr << "Cannot use " << name |
+ << " and generate code or descriptors at the same time." |
+ << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
mode_ = (name == "--encode") ? MODE_ENCODE : MODE_DECODE; |
if (value.empty() && name != "--decode_raw") { |
- cerr << "Type name for " << name << " cannot be blank." << endl; |
+ std::cerr << "Type name for " << name << " cannot be blank." << std::endl; |
if (name == "--decode") { |
- cerr << "To decode an unknown message, use --decode_raw." << endl; |
+ std::cerr << "To decode an unknown message, use --decode_raw." |
+ << std::endl; |
} |
return PARSE_ARGUMENT_FAIL; |
} else if (!value.empty() && name == "--decode_raw") { |
- cerr << "--decode_raw does not take a parameter." << endl; |
+ std::cerr << "--decode_raw does not take a parameter." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
@@ -1031,13 +1228,13 @@ CommandLineInterface::InterpretArgument(const string& name, |
} else if (value == "msvs") { |
error_format_ = ERROR_FORMAT_MSVS; |
} else { |
- cerr << "Unknown error format: " << value << endl; |
+ std::cerr << "Unknown error format: " << value << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
} else if (name == "--plugin") { |
if (plugin_prefix_.empty()) { |
- cerr << "This compiler does not support plugins." << endl; |
+ std::cerr << "This compiler does not support plugins." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
@@ -1061,6 +1258,21 @@ CommandLineInterface::InterpretArgument(const string& name, |
plugins_[plugin_name] = path; |
+ } else if (name == "--print_free_field_numbers") { |
+ if (mode_ != MODE_COMPILE) { |
+ std::cerr << "Cannot use " << name |
+ << " and use --encode, --decode or print " |
+ << "other info at the same time." << std::endl; |
+ return PARSE_ARGUMENT_FAIL; |
+ } |
+ if (!output_directives_.empty() || !descriptor_set_name_.empty()) { |
+ std::cerr << "Cannot use " << name |
+ << " and generate code or descriptors at the same time." |
+ << std::endl; |
+ return PARSE_ARGUMENT_FAIL; |
+ } |
+ mode_ = MODE_PRINT; |
+ print_mode_ = PRINT_FREE_FIELDS; |
} else { |
// Some other flag. Look it up in the generators list. |
const GeneratorInfo* generator_info = |
@@ -1070,7 +1282,7 @@ CommandLineInterface::InterpretArgument(const string& name, |
// Check if it's a generator option flag. |
generator_info = FindOrNull(generators_by_option_name_, name); |
if (generator_info == NULL) { |
- cerr << "Unknown flag: " << name << endl; |
+ std::cerr << "Unknown flag: " << name << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} else { |
string* parameters = &generator_parameters_[generator_info->flag_name]; |
@@ -1082,8 +1294,8 @@ CommandLineInterface::InterpretArgument(const string& name, |
} else { |
// It's an output flag. Add it to the output directives. |
if (mode_ != MODE_COMPILE) { |
- cerr << "Cannot use --encode or --decode and generate code at the " |
- "same time." << endl; |
+ std::cerr << "Cannot use --encode, --decode or print .proto info and " |
+ "generate code at the same time." << std::endl; |
return PARSE_ARGUMENT_FAIL; |
} |
@@ -1115,7 +1327,7 @@ CommandLineInterface::InterpretArgument(const string& name, |
void CommandLineInterface::PrintHelpText() { |
// Sorry for indentation here; line wrapping would be uglier. |
- cerr << |
+ std::cerr << |
"Usage: " << executable_name_ << " [OPTION] PROTO_FILES\n" |
"Parse PROTO_FILES and generate output based on the options given:\n" |
" -IPATH, --proto_path=PATH Specify the directory in which to search for\n" |
@@ -1149,11 +1361,20 @@ void CommandLineInterface::PrintHelpText() { |
" include information about the original\n" |
" location of each decl in the source file as\n" |
" well as surrounding comments.\n" |
+" --dependency_out=FILE Write a dependency output file in the format\n" |
+" expected by make. This writes the transitive\n" |
+" set of input file paths to FILE\n" |
" --error_format=FORMAT Set the format in which to print errors.\n" |
" FORMAT may be 'gcc' (the default) or 'msvs'\n" |
-" (Microsoft Visual Studio format)." << endl; |
+" (Microsoft Visual Studio format).\n" |
+" --print_free_field_numbers Print the free field numbers of the messages\n" |
+" defined in the given proto files. Groups share\n" |
+" the same field number space with the parent \n" |
+" message. Extension ranges are counted as \n" |
+" occupied fields numbers.\n" |
+ << std::endl; |
if (!plugin_prefix_.empty()) { |
- cerr << |
+ std::cerr << |
" --plugin=EXECUTABLE Specifies a plugin executable to use.\n" |
" Normally, protoc searches the PATH for\n" |
" plugins, but you may specify additional\n" |
@@ -1161,7 +1382,7 @@ void CommandLineInterface::PrintHelpText() { |
" Additionally, EXECUTABLE may be of the form\n" |
" NAME=PATH, in which case the given plugin name\n" |
" is mapped to the given executable even if\n" |
-" the executable's own name differs." << endl; |
+" the executable's own name differs." << std::endl; |
} |
for (GeneratorMap::iterator iter = generators_by_flag_name_.begin(); |
@@ -1169,9 +1390,9 @@ void CommandLineInterface::PrintHelpText() { |
// FIXME(kenton): If the text is long enough it will wrap, which is ugly, |
// but fixing this nicely (e.g. splitting on spaces) is probably more |
// trouble than it's worth. |
- cerr << " " << iter->first << "=OUT_DIR " |
- << string(19 - iter->first.size(), ' ') // Spaces for alignment. |
- << iter->second.help_text << endl; |
+ std::cerr << " " << iter->first << "=OUT_DIR " |
+ << string(19 - iter->first.size(), ' ') // Spaces for alignment. |
+ << iter->second.help_text << std::endl; |
} |
} |
@@ -1194,7 +1415,7 @@ bool CommandLineInterface::GenerateOutput( |
if (!GeneratePluginOutput(parsed_files, plugin_name, |
output_directive.parameter, |
generator_context, &error)) { |
- cerr << output_directive.name << ": " << error << endl; |
+ std::cerr << output_directive.name << ": " << error << std::endl; |
return false; |
} |
} else { |
@@ -1206,14 +1427,96 @@ bool CommandLineInterface::GenerateOutput( |
} |
parameters.append(generator_parameters_[output_directive.name]); |
} |
- for (int i = 0; i < parsed_files.size(); i++) { |
- if (!output_directive.generator->Generate(parsed_files[i], parameters, |
- generator_context, &error)) { |
- // Generator returned an error. |
- cerr << output_directive.name << ": " << parsed_files[i]->name() << ": " |
- << error << endl; |
- return false; |
+ if (output_directive.generator->HasGenerateAll()) { |
+ if (!output_directive.generator->GenerateAll( |
+ parsed_files, parameters, generator_context, &error)) { |
+ // Generator returned an error. |
+ std::cerr << output_directive.name << ": " |
+ << ": " << error << std::endl; |
+ return false; |
} |
+ } else { |
+ for (int i = 0; i < parsed_files.size(); i++) { |
+ if (!output_directive.generator->Generate(parsed_files[i], parameters, |
+ generator_context, &error)) { |
+ // Generator returned an error. |
+ std::cerr << output_directive.name << ": " << parsed_files[i]->name() |
+ << ": " << error << std::endl; |
+ return false; |
+ } |
+ } |
+ } |
+ } |
+ |
+ return true; |
+} |
+ |
+bool CommandLineInterface::GenerateDependencyManifestFile( |
+ const vector<const FileDescriptor*>& parsed_files, |
+ const GeneratorContextMap& output_directories, |
+ DiskSourceTree* source_tree) { |
+ FileDescriptorSet file_set; |
+ |
+ set<const FileDescriptor*> already_seen; |
+ for (int i = 0; i < parsed_files.size(); i++) { |
+ GetTransitiveDependencies(parsed_files[i], |
+ false, |
+ false, |
+ &already_seen, |
+ file_set.mutable_file()); |
+ } |
+ |
+ vector<string> output_filenames; |
+ for (GeneratorContextMap::const_iterator iter = output_directories.begin(); |
+ iter != output_directories.end(); ++iter) { |
+ const string& location = iter->first; |
+ GeneratorContextImpl* directory = iter->second; |
+ vector<string> relative_output_filenames; |
+ directory->GetOutputFilenames(&relative_output_filenames); |
+ for (int i = 0; i < relative_output_filenames.size(); i++) { |
+ string output_filename = location + relative_output_filenames[i]; |
+ if (output_filename.compare(0, 2, "./") == 0) { |
+ output_filename = output_filename.substr(2); |
+ } |
+ output_filenames.push_back(output_filename); |
+ } |
+ } |
+ |
+ int fd; |
+ do { |
+ fd = open(dependency_out_name_.c_str(), |
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); |
+ } while (fd < 0 && errno == EINTR); |
+ |
+ if (fd < 0) { |
+ perror(dependency_out_name_.c_str()); |
+ return false; |
+ } |
+ |
+ io::FileOutputStream out(fd); |
+ io::Printer printer(&out, '$'); |
+ |
+ for (int i = 0; i < output_filenames.size(); i++) { |
+ printer.Print(output_filenames[i].c_str()); |
+ if (i == output_filenames.size() - 1) { |
+ printer.Print(":"); |
+ } else { |
+ printer.Print(" \\\n"); |
+ } |
+ } |
+ |
+ for (int i = 0; i < file_set.file_size(); i++) { |
+ const FileDescriptorProto& file = file_set.file(i); |
+ const string& virtual_file = file.name(); |
+ string disk_file; |
+ if (source_tree && |
+ source_tree->VirtualFileToDiskFile(virtual_file, &disk_file)) { |
+ printer.Print(" $disk_file$", "disk_file", disk_file); |
+ if (i < file_set.file_size() - 1) printer.Print("\\\n"); |
+ } else { |
+ std::cerr << "Unable to identify path for file " << virtual_file |
+ << std::endl; |
+ return false; |
} |
} |
@@ -1238,6 +1541,7 @@ bool CommandLineInterface::GeneratePluginOutput( |
for (int i = 0; i < parsed_files.size(); i++) { |
request.add_file_to_generate(parsed_files[i]->name()); |
GetTransitiveDependencies(parsed_files[i], |
+ true, // Include json_name for plugins. |
true, // Include source code info. |
&already_seen, request.mutable_proto_file()); |
} |
@@ -1259,7 +1563,7 @@ bool CommandLineInterface::GeneratePluginOutput( |
// Write the files. We do this even if there was a generator error in order |
// to match the behavior of a compiled-in generator. |
- scoped_ptr<io::ZeroCopyOutputStream> current_output; |
+ google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> current_output; |
for (int i = 0; i < response.file_size(); i++) { |
const CodeGeneratorResponse::File& output_file = response.file(i); |
@@ -1303,12 +1607,12 @@ bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) { |
// Look up the type. |
const Descriptor* type = pool->FindMessageTypeByName(codec_type_); |
if (type == NULL) { |
- cerr << "Type not defined: " << codec_type_ << endl; |
+ std::cerr << "Type not defined: " << codec_type_ << std::endl; |
return false; |
} |
DynamicMessageFactory dynamic_factory(pool); |
- scoped_ptr<Message> message(dynamic_factory.GetPrototype(type)->New()); |
+ google::protobuf::scoped_ptr<Message> message(dynamic_factory.GetPrototype(type)->New()); |
if (mode_ == MODE_ENCODE) { |
SetFdToTextMode(STDIN_FILENO); |
@@ -1329,32 +1633,32 @@ bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) { |
parser.AllowPartialMessage(true); |
if (!parser.Parse(&in, message.get())) { |
- cerr << "Failed to parse input." << endl; |
+ std::cerr << "Failed to parse input." << std::endl; |
return false; |
} |
} else { |
// Input is binary. |
if (!message->ParsePartialFromZeroCopyStream(&in)) { |
- cerr << "Failed to parse input." << endl; |
+ std::cerr << "Failed to parse input." << std::endl; |
return false; |
} |
} |
if (!message->IsInitialized()) { |
- cerr << "warning: Input message is missing required fields: " |
- << message->InitializationErrorString() << endl; |
+ std::cerr << "warning: Input message is missing required fields: " |
+ << message->InitializationErrorString() << std::endl; |
} |
if (mode_ == MODE_ENCODE) { |
// Output is binary. |
if (!message->SerializePartialToZeroCopyStream(&out)) { |
- cerr << "output: I/O error." << endl; |
+ std::cerr << "output: I/O error." << std::endl; |
return false; |
} |
} else { |
// Output is text. |
if (!TextFormat::Print(*message, &out)) { |
- cerr << "output: I/O error." << endl; |
+ std::cerr << "output: I/O error." << std::endl; |
return false; |
} |
} |
@@ -1370,13 +1674,19 @@ bool CommandLineInterface::WriteDescriptorSet( |
set<const FileDescriptor*> already_seen; |
for (int i = 0; i < parsed_files.size(); i++) { |
GetTransitiveDependencies(parsed_files[i], |
+ true, // Include json_name |
source_info_in_descriptor_set_, |
&already_seen, file_set.mutable_file()); |
} |
} else { |
+ set<const FileDescriptor*> already_seen; |
for (int i = 0; i < parsed_files.size(); i++) { |
+ if (!already_seen.insert(parsed_files[i]).second) { |
+ continue; |
+ } |
FileDescriptorProto* file_proto = file_set.add_file(); |
parsed_files[i]->CopyTo(file_proto); |
+ parsed_files[i]->CopyJsonNameTo(file_proto); |
if (source_info_in_descriptor_set_) { |
parsed_files[i]->CopySourceCodeInfoTo(file_proto); |
} |
@@ -1396,12 +1706,14 @@ bool CommandLineInterface::WriteDescriptorSet( |
io::FileOutputStream out(fd); |
if (!file_set.SerializeToZeroCopyStream(&out)) { |
- cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) << endl; |
+ std::cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) |
+ << std::endl; |
out.Close(); |
return false; |
} |
if (!out.Close()) { |
- cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) << endl; |
+ std::cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) |
+ << std::endl; |
return false; |
} |
@@ -1409,7 +1721,9 @@ bool CommandLineInterface::WriteDescriptorSet( |
} |
void CommandLineInterface::GetTransitiveDependencies( |
- const FileDescriptor* file, bool include_source_code_info, |
+ const FileDescriptor* file, |
+ bool include_json_name, |
+ bool include_source_code_info, |
set<const FileDescriptor*>* already_seen, |
RepeatedPtrField<FileDescriptorProto>* output) { |
if (!already_seen->insert(file).second) { |
@@ -1419,18 +1733,134 @@ void CommandLineInterface::GetTransitiveDependencies( |
// Add all dependencies. |
for (int i = 0; i < file->dependency_count(); i++) { |
- GetTransitiveDependencies(file->dependency(i), include_source_code_info, |
+ GetTransitiveDependencies(file->dependency(i), |
+ include_json_name, |
+ include_source_code_info, |
already_seen, output); |
} |
// Add this file. |
FileDescriptorProto* new_descriptor = output->Add(); |
file->CopyTo(new_descriptor); |
+ if (include_json_name) { |
+ file->CopyJsonNameTo(new_descriptor); |
+ } |
if (include_source_code_info) { |
file->CopySourceCodeInfoTo(new_descriptor); |
} |
} |
+namespace { |
+ |
+// Utility function for PrintFreeFieldNumbers. |
+// Stores occupied ranges into the ranges parameter, and next level of sub |
+// message types into the nested_messages parameter. The FieldRange is left |
+// inclusive, right exclusive. i.e. [a, b). |
+// |
+// Nested Messages: |
+// Note that it only stores the nested message type, iff the nested type is |
+// either a direct child of the given descriptor, or the nested type is a |
+// decendent of the given descriptor and all the nodes between the |
+// nested type and the given descriptor are group types. e.g. |
+// |
+// message Foo { |
+// message Bar { |
+// message NestedBar {} |
+// } |
+// group Baz = 1 { |
+// group NestedBazGroup = 2 { |
+// message Quz { |
+// message NestedQuz {} |
+// } |
+// } |
+// message NestedBaz {} |
+// } |
+// } |
+// |
+// In this case, Bar, Quz and NestedBaz will be added into the nested types. |
+// Since free field numbers of group types will not be printed, this makes sure |
+// the nested message types in groups will not be dropped. The nested_messages |
+// parameter will contain the direct children (when groups are ignored in the |
+// tree) of the given descriptor for the caller to traverse. The declaration |
+// order of the nested messages is also preserved. |
+typedef pair<int, int> FieldRange; |
+void GatherOccupiedFieldRanges(const Descriptor* descriptor, |
+ set<FieldRange>* ranges, |
+ vector<const Descriptor*>* nested_messages) { |
+ set<const Descriptor*> groups; |
+ for (int i = 0; i < descriptor->field_count(); ++i) { |
+ const FieldDescriptor* fd = descriptor->field(i); |
+ ranges->insert(FieldRange(fd->number(), fd->number() + 1)); |
+ if (fd->type() == FieldDescriptor::TYPE_GROUP) { |
+ groups.insert(fd->message_type()); |
+ } |
+ } |
+ for (int i = 0; i < descriptor->extension_range_count(); ++i) { |
+ ranges->insert(FieldRange(descriptor->extension_range(i)->start, |
+ descriptor->extension_range(i)->end)); |
+ } |
+ for (int i = 0; i < descriptor->reserved_range_count(); ++i) { |
+ ranges->insert(FieldRange(descriptor->reserved_range(i)->start, |
+ descriptor->reserved_range(i)->end)); |
+ } |
+ // Handle the nested messages/groups in declaration order to make it |
+ // post-order strict. |
+ for (int i = 0; i < descriptor->nested_type_count(); ++i) { |
+ const Descriptor* nested_desc = descriptor->nested_type(i); |
+ if (groups.find(nested_desc) != groups.end()) { |
+ GatherOccupiedFieldRanges(nested_desc, ranges, nested_messages); |
+ } else { |
+ nested_messages->push_back(nested_desc); |
+ } |
+ } |
+} |
+ |
+// Utility function for PrintFreeFieldNumbers. |
+// Actually prints the formatted free field numbers for given message name and |
+// occupied ranges. |
+void FormatFreeFieldNumbers(const string& name, |
+ const set<FieldRange>& ranges) { |
+ string output; |
+ StringAppendF(&output, "%-35s free:", name.c_str()); |
+ int next_free_number = 1; |
+ for (set<FieldRange>::const_iterator i = ranges.begin(); |
+ i != ranges.end(); ++i) { |
+ // This happens when groups re-use parent field numbers, in which |
+ // case we skip the FieldRange entirely. |
+ if (next_free_number >= i->second) continue; |
+ |
+ if (next_free_number < i->first) { |
+ if (next_free_number + 1 == i->first) { |
+ // Singleton |
+ StringAppendF(&output, " %d", next_free_number); |
+ } else { |
+ // Range |
+ StringAppendF(&output, " %d-%d", next_free_number, i->first - 1); |
+ } |
+ } |
+ next_free_number = i->second; |
+ } |
+ if (next_free_number <= FieldDescriptor::kMaxNumber) { |
+ StringAppendF(&output, " %d-INF", next_free_number); |
+ } |
+ std::cout << output << std::endl; |
+} |
+ |
+} // namespace |
+ |
+void CommandLineInterface::PrintFreeFieldNumbers( |
+ const Descriptor* descriptor) { |
+ set<FieldRange> ranges; |
+ vector<const Descriptor*> nested_messages; |
+ GatherOccupiedFieldRanges(descriptor, &ranges, &nested_messages); |
+ |
+ for (int i = 0; i < nested_messages.size(); ++i) { |
+ PrintFreeFieldNumbers(nested_messages[i]); |
+ } |
+ FormatFreeFieldNumbers(descriptor->full_name(), ranges); |
+} |
+ |
+ |
} // namespace compiler |
} // namespace protobuf |