| 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
|
|
|