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