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