/thirdparty/breakpad/third_party/protobuf/protobuf/src/google/protobuf/compiler/command_line_interface.cc

http://github.com/tomahawk-player/tomahawk · C++ · 1357 lines · 987 code · 188 blank · 182 comment · 303 complexity · abae867cde3a8ecd751d040892ded3e0 MD5 · raw file

  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // http://code.google.com/p/protobuf/
  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. // Author: kenton@google.com (Kenton Varda)
  31. // Based on original Protocol Buffers design by
  32. // Sanjay Ghemawat, Jeff Dean, and others.
  33. #include <google/protobuf/compiler/command_line_interface.h>
  34. #include <stdio.h>
  35. #include <sys/types.h>
  36. #include <sys/stat.h>
  37. #include <fcntl.h>
  38. #ifdef _MSC_VER
  39. #include <io.h>
  40. #include <direct.h>
  41. #else
  42. #include <unistd.h>
  43. #endif
  44. #include <errno.h>
  45. #include <iostream>
  46. #include <ctype.h>
  47. #include <google/protobuf/stubs/hash.h>
  48. #include <google/protobuf/stubs/common.h>
  49. #include <google/protobuf/compiler/importer.h>
  50. #include <google/protobuf/compiler/code_generator.h>
  51. #include <google/protobuf/compiler/plugin.pb.h>
  52. #include <google/protobuf/compiler/subprocess.h>
  53. #include <google/protobuf/compiler/zip_writer.h>
  54. #include <google/protobuf/descriptor.h>
  55. #include <google/protobuf/text_format.h>
  56. #include <google/protobuf/dynamic_message.h>
  57. #include <google/protobuf/io/zero_copy_stream_impl.h>
  58. #include <google/protobuf/io/printer.h>
  59. #include <google/protobuf/stubs/strutil.h>
  60. #include <google/protobuf/stubs/substitute.h>
  61. #include <google/protobuf/stubs/map-util.h>
  62. #include <google/protobuf/stubs/stl_util-inl.h>
  63. namespace google {
  64. namespace protobuf {
  65. namespace compiler {
  66. #if defined(_WIN32)
  67. #define mkdir(name, mode) mkdir(name)
  68. #ifndef W_OK
  69. #define W_OK 02 // not defined by MSVC for whatever reason
  70. #endif
  71. #ifndef F_OK
  72. #define F_OK 00 // not defined by MSVC for whatever reason
  73. #endif
  74. #ifndef STDIN_FILENO
  75. #define STDIN_FILENO 0
  76. #endif
  77. #ifndef STDOUT_FILENO
  78. #define STDOUT_FILENO 1
  79. #endif
  80. #endif
  81. #ifndef O_BINARY
  82. #ifdef _O_BINARY
  83. #define O_BINARY _O_BINARY
  84. #else
  85. #define O_BINARY 0 // If this isn't defined, the platform doesn't need it.
  86. #endif
  87. #endif
  88. namespace {
  89. #if defined(_WIN32) && !defined(__CYGWIN__)
  90. static const char* kPathSeparator = ";";
  91. #else
  92. static const char* kPathSeparator = ":";
  93. #endif
  94. // Returns true if the text looks like a Windows-style absolute path, starting
  95. // with a drive letter. Example: "C:\foo". TODO(kenton): Share this with
  96. // copy in importer.cc?
  97. static bool IsWindowsAbsolutePath(const string& text) {
  98. #if defined(_WIN32) || defined(__CYGWIN__)
  99. return text.size() >= 3 && text[1] == ':' &&
  100. isalpha(text[0]) &&
  101. (text[2] == '/' || text[2] == '\\') &&
  102. text.find_last_of(':') == 1;
  103. #else
  104. return false;
  105. #endif
  106. }
  107. void SetFdToTextMode(int fd) {
  108. #ifdef _WIN32
  109. if (_setmode(fd, _O_TEXT) == -1) {
  110. // This should never happen, I think.
  111. GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_TEXT): " << strerror(errno);
  112. }
  113. #endif
  114. // (Text and binary are the same on non-Windows platforms.)
  115. }
  116. void SetFdToBinaryMode(int fd) {
  117. #ifdef _WIN32
  118. if (_setmode(fd, _O_BINARY) == -1) {
  119. // This should never happen, I think.
  120. GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_BINARY): " << strerror(errno);
  121. }
  122. #endif
  123. // (Text and binary are the same on non-Windows platforms.)
  124. }
  125. void AddTrailingSlash(string* path) {
  126. if (!path->empty() && path->at(path->size() - 1) != '/') {
  127. path->push_back('/');
  128. }
  129. }
  130. bool VerifyDirectoryExists(const string& path) {
  131. if (path.empty()) return true;
  132. if (access(path.c_str(), W_OK) == -1) {
  133. cerr << path << ": " << strerror(errno) << endl;
  134. return false;
  135. } else {
  136. return true;
  137. }
  138. }
  139. // Try to create the parent directory of the given file, creating the parent's
  140. // parent if necessary, and so on. The full file name is actually
  141. // (prefix + filename), but we assume |prefix| already exists and only create
  142. // directories listed in |filename|.
  143. bool TryCreateParentDirectory(const string& prefix, const string& filename) {
  144. // Recursively create parent directories to the output file.
  145. vector<string> parts;
  146. SplitStringUsing(filename, "/", &parts);
  147. string path_so_far = prefix;
  148. for (int i = 0; i < parts.size() - 1; i++) {
  149. path_so_far += parts[i];
  150. if (mkdir(path_so_far.c_str(), 0777) != 0) {
  151. if (errno != EEXIST) {
  152. cerr << filename << ": while trying to create directory "
  153. << path_so_far << ": " << strerror(errno) << endl;
  154. return false;
  155. }
  156. }
  157. path_so_far += '/';
  158. }
  159. return true;
  160. }
  161. } // namespace
  162. // A MultiFileErrorCollector that prints errors to stderr.
  163. class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector,
  164. public io::ErrorCollector {
  165. public:
  166. ErrorPrinter(ErrorFormat format, DiskSourceTree *tree = NULL)
  167. : format_(format), tree_(tree) {}
  168. ~ErrorPrinter() {}
  169. // implements MultiFileErrorCollector ------------------------------
  170. void AddError(const string& filename, int line, int column,
  171. const string& message) {
  172. // Print full path when running under MSVS
  173. string dfile;
  174. if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
  175. tree_ != NULL &&
  176. tree_->VirtualFileToDiskFile(filename, &dfile)) {
  177. cerr << dfile;
  178. } else {
  179. cerr << filename;
  180. }
  181. // Users typically expect 1-based line/column numbers, so we add 1
  182. // to each here.
  183. if (line != -1) {
  184. // Allow for both GCC- and Visual-Studio-compatible output.
  185. switch (format_) {
  186. case CommandLineInterface::ERROR_FORMAT_GCC:
  187. cerr << ":" << (line + 1) << ":" << (column + 1);
  188. break;
  189. case CommandLineInterface::ERROR_FORMAT_MSVS:
  190. cerr << "(" << (line + 1) << ") : error in column=" << (column + 1);
  191. break;
  192. }
  193. }
  194. cerr << ": " << message << endl;
  195. }
  196. // implements io::ErrorCollector -----------------------------------
  197. void AddError(int line, int column, const string& message) {
  198. AddError("input", line, column, message);
  199. }
  200. private:
  201. const ErrorFormat format_;
  202. DiskSourceTree *tree_;
  203. };
  204. // -------------------------------------------------------------------
  205. // A GeneratorContext implementation that buffers files in memory, then dumps
  206. // them all to disk on demand.
  207. class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
  208. public:
  209. GeneratorContextImpl(const vector<const FileDescriptor*>& parsed_files);
  210. ~GeneratorContextImpl();
  211. // Write all files in the directory to disk at the given output location,
  212. // which must end in a '/'.
  213. bool WriteAllToDisk(const string& prefix);
  214. // Write the contents of this directory to a ZIP-format archive with the
  215. // given name.
  216. bool WriteAllToZip(const string& filename);
  217. // Add a boilerplate META-INF/MANIFEST.MF file as required by the Java JAR
  218. // format, unless one has already been written.
  219. void AddJarManifest();
  220. // implements GeneratorContext --------------------------------------
  221. io::ZeroCopyOutputStream* Open(const string& filename);
  222. io::ZeroCopyOutputStream* OpenForInsert(
  223. const string& filename, const string& insertion_point);
  224. void ListParsedFiles(vector<const FileDescriptor*>* output) {
  225. *output = parsed_files_;
  226. }
  227. private:
  228. friend class MemoryOutputStream;
  229. // map instead of hash_map so that files are written in order (good when
  230. // writing zips).
  231. map<string, string*> files_;
  232. const vector<const FileDescriptor*>& parsed_files_;
  233. bool had_error_;
  234. };
  235. class CommandLineInterface::MemoryOutputStream
  236. : public io::ZeroCopyOutputStream {
  237. public:
  238. MemoryOutputStream(GeneratorContextImpl* directory, const string& filename);
  239. MemoryOutputStream(GeneratorContextImpl* directory, const string& filename,
  240. const string& insertion_point);
  241. virtual ~MemoryOutputStream();
  242. // implements ZeroCopyOutputStream ---------------------------------
  243. virtual bool Next(void** data, int* size) { return inner_->Next(data, size); }
  244. virtual void BackUp(int count) { inner_->BackUp(count); }
  245. virtual int64 ByteCount() const { return inner_->ByteCount(); }
  246. private:
  247. // Where to insert the string when it's done.
  248. GeneratorContextImpl* directory_;
  249. string filename_;
  250. string insertion_point_;
  251. // The string we're building.
  252. string data_;
  253. // StringOutputStream writing to data_.
  254. scoped_ptr<io::StringOutputStream> inner_;
  255. };
  256. // -------------------------------------------------------------------
  257. CommandLineInterface::GeneratorContextImpl::GeneratorContextImpl(
  258. const vector<const FileDescriptor*>& parsed_files)
  259. : parsed_files_(parsed_files),
  260. had_error_(false) {
  261. }
  262. CommandLineInterface::GeneratorContextImpl::~GeneratorContextImpl() {
  263. STLDeleteValues(&files_);
  264. }
  265. bool CommandLineInterface::GeneratorContextImpl::WriteAllToDisk(
  266. const string& prefix) {
  267. if (had_error_) {
  268. return false;
  269. }
  270. if (!VerifyDirectoryExists(prefix)) {
  271. return false;
  272. }
  273. for (map<string, string*>::const_iterator iter = files_.begin();
  274. iter != files_.end(); ++iter) {
  275. const string& relative_filename = iter->first;
  276. const char* data = iter->second->data();
  277. int size = iter->second->size();
  278. if (!TryCreateParentDirectory(prefix, relative_filename)) {
  279. return false;
  280. }
  281. string filename = prefix + relative_filename;
  282. // Create the output file.
  283. int file_descriptor;
  284. do {
  285. file_descriptor =
  286. open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
  287. } while (file_descriptor < 0 && errno == EINTR);
  288. if (file_descriptor < 0) {
  289. int error = errno;
  290. cerr << filename << ": " << strerror(error);
  291. return false;
  292. }
  293. // Write the file.
  294. while (size > 0) {
  295. int write_result;
  296. do {
  297. write_result = write(file_descriptor, data, size);
  298. } while (write_result < 0 && errno == EINTR);
  299. if (write_result <= 0) {
  300. // Write error.
  301. // FIXME(kenton): According to the man page, if write() returns zero,
  302. // there was no error; write() simply did not write anything. It's
  303. // unclear under what circumstances this might happen, but presumably
  304. // errno won't be set in this case. I am confused as to how such an
  305. // event should be handled. For now I'm treating it as an error,
  306. // since retrying seems like it could lead to an infinite loop. I
  307. // suspect this never actually happens anyway.
  308. if (write_result < 0) {
  309. int error = errno;
  310. cerr << filename << ": write: " << strerror(error);
  311. } else {
  312. cerr << filename << ": write() returned zero?" << endl;
  313. }
  314. return false;
  315. }
  316. data += write_result;
  317. size -= write_result;
  318. }
  319. if (close(file_descriptor) != 0) {
  320. int error = errno;
  321. cerr << filename << ": close: " << strerror(error);
  322. return false;
  323. }
  324. }
  325. return true;
  326. }
  327. bool CommandLineInterface::GeneratorContextImpl::WriteAllToZip(
  328. const string& filename) {
  329. if (had_error_) {
  330. return false;
  331. }
  332. // Create the output file.
  333. int file_descriptor;
  334. do {
  335. file_descriptor =
  336. open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
  337. } while (file_descriptor < 0 && errno == EINTR);
  338. if (file_descriptor < 0) {
  339. int error = errno;
  340. cerr << filename << ": " << strerror(error);
  341. return false;
  342. }
  343. // Create the ZipWriter
  344. io::FileOutputStream stream(file_descriptor);
  345. ZipWriter zip_writer(&stream);
  346. for (map<string, string*>::const_iterator iter = files_.begin();
  347. iter != files_.end(); ++iter) {
  348. zip_writer.Write(iter->first, *iter->second);
  349. }
  350. zip_writer.WriteDirectory();
  351. if (stream.GetErrno() != 0) {
  352. cerr << filename << ": " << strerror(stream.GetErrno()) << endl;
  353. }
  354. if (!stream.Close()) {
  355. cerr << filename << ": " << strerror(stream.GetErrno()) << endl;
  356. }
  357. return true;
  358. }
  359. void CommandLineInterface::GeneratorContextImpl::AddJarManifest() {
  360. string** map_slot = &files_["META-INF/MANIFEST.MF"];
  361. if (*map_slot == NULL) {
  362. *map_slot = new string(
  363. "Manifest-Version: 1.0\n"
  364. "Created-By: 1.6.0 (protoc)\n"
  365. "\n");
  366. }
  367. }
  368. io::ZeroCopyOutputStream* CommandLineInterface::GeneratorContextImpl::Open(
  369. const string& filename) {
  370. return new MemoryOutputStream(this, filename);
  371. }
  372. io::ZeroCopyOutputStream*
  373. CommandLineInterface::GeneratorContextImpl::OpenForInsert(
  374. const string& filename, const string& insertion_point) {
  375. return new MemoryOutputStream(this, filename, insertion_point);
  376. }
  377. // -------------------------------------------------------------------
  378. CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
  379. GeneratorContextImpl* directory, const string& filename)
  380. : directory_(directory),
  381. filename_(filename),
  382. inner_(new io::StringOutputStream(&data_)) {
  383. }
  384. CommandLineInterface::MemoryOutputStream::MemoryOutputStream(
  385. GeneratorContextImpl* directory, const string& filename,
  386. const string& insertion_point)
  387. : directory_(directory),
  388. filename_(filename),
  389. insertion_point_(insertion_point),
  390. inner_(new io::StringOutputStream(&data_)) {
  391. }
  392. CommandLineInterface::MemoryOutputStream::~MemoryOutputStream() {
  393. // Make sure all data has been written.
  394. inner_.reset();
  395. // Insert into the directory.
  396. string** map_slot = &directory_->files_[filename_];
  397. if (insertion_point_.empty()) {
  398. // This was just a regular Open().
  399. if (*map_slot != NULL) {
  400. cerr << filename_ << ": Tried to write the same file twice." << endl;
  401. directory_->had_error_ = true;
  402. return;
  403. }
  404. *map_slot = new string;
  405. (*map_slot)->swap(data_);
  406. } else {
  407. // This was an OpenForInsert().
  408. // If the data doens't end with a clean line break, add one.
  409. if (!data_.empty() && data_[data_.size() - 1] != '\n') {
  410. data_.push_back('\n');
  411. }
  412. // Find the file we are going to insert into.
  413. if (*map_slot == NULL) {
  414. cerr << filename_ << ": Tried to insert into file that doesn't exist."
  415. << endl;
  416. directory_->had_error_ = true;
  417. return;
  418. }
  419. string* target = *map_slot;
  420. // Find the insertion point.
  421. string magic_string = strings::Substitute(
  422. "@@protoc_insertion_point($0)", insertion_point_);
  423. string::size_type pos = target->find(magic_string);
  424. if (pos == string::npos) {
  425. cerr << filename_ << ": insertion point \"" << insertion_point_
  426. << "\" not found." << endl;
  427. directory_->had_error_ = true;
  428. return;
  429. }
  430. // Seek backwards to the beginning of the line, which is where we will
  431. // insert the data. Note that this has the effect of pushing the insertion
  432. // point down, so the data is inserted before it. This is intentional
  433. // because it means that multiple insertions at the same point will end
  434. // up in the expected order in the final output.
  435. pos = target->find_last_of('\n', pos);
  436. if (pos == string::npos) {
  437. // Insertion point is on the first line.
  438. pos = 0;
  439. } else {
  440. // Advance to character after '\n'.
  441. ++pos;
  442. }
  443. // Extract indent.
  444. string indent_(*target, pos, target->find_first_not_of(" \t", pos) - pos);
  445. if (indent_.empty()) {
  446. // No indent. This makes things easier.
  447. target->insert(pos, data_);
  448. } else {
  449. // Calculate how much space we need.
  450. int indent_size = 0;
  451. for (int i = 0; i < data_.size(); i++) {
  452. if (data_[i] == '\n') indent_size += indent_.size();
  453. }
  454. // Make a hole for it.
  455. target->insert(pos, data_.size() + indent_size, '\0');
  456. // Now copy in the data.
  457. string::size_type data_pos = 0;
  458. char* target_ptr = string_as_array(target) + pos;
  459. while (data_pos < data_.size()) {
  460. // Copy indent.
  461. memcpy(target_ptr, indent_.data(), indent_.size());
  462. target_ptr += indent_.size();
  463. // Copy line from data_.
  464. // We already guaranteed that data_ ends with a newline (above), so this
  465. // search can't fail.
  466. string::size_type line_length =
  467. data_.find_first_of('\n', data_pos) + 1 - data_pos;
  468. memcpy(target_ptr, data_.data() + data_pos, line_length);
  469. target_ptr += line_length;
  470. data_pos += line_length;
  471. }
  472. GOOGLE_CHECK_EQ(target_ptr,
  473. string_as_array(target) + pos + data_.size() + indent_size);
  474. }
  475. }
  476. }
  477. // ===================================================================
  478. CommandLineInterface::CommandLineInterface()
  479. : mode_(MODE_COMPILE),
  480. error_format_(ERROR_FORMAT_GCC),
  481. imports_in_descriptor_set_(false),
  482. disallow_services_(false),
  483. inputs_are_proto_path_relative_(false) {}
  484. CommandLineInterface::~CommandLineInterface() {}
  485. void CommandLineInterface::RegisterGenerator(const string& flag_name,
  486. CodeGenerator* generator,
  487. const string& help_text) {
  488. GeneratorInfo info;
  489. info.generator = generator;
  490. info.help_text = help_text;
  491. generators_[flag_name] = info;
  492. }
  493. void CommandLineInterface::AllowPlugins(const string& exe_name_prefix) {
  494. plugin_prefix_ = exe_name_prefix;
  495. }
  496. int CommandLineInterface::Run(int argc, const char* const argv[]) {
  497. Clear();
  498. if (!ParseArguments(argc, argv)) return 1;
  499. // Set up the source tree.
  500. DiskSourceTree source_tree;
  501. for (int i = 0; i < proto_path_.size(); i++) {
  502. source_tree.MapPath(proto_path_[i].first, proto_path_[i].second);
  503. }
  504. // Map input files to virtual paths if necessary.
  505. if (!inputs_are_proto_path_relative_) {
  506. if (!MakeInputsBeProtoPathRelative(&source_tree)) {
  507. return 1;
  508. }
  509. }
  510. // Allocate the Importer.
  511. ErrorPrinter error_collector(error_format_, &source_tree);
  512. Importer importer(&source_tree, &error_collector);
  513. vector<const FileDescriptor*> parsed_files;
  514. // Parse each file.
  515. for (int i = 0; i < input_files_.size(); i++) {
  516. // Import the file.
  517. const FileDescriptor* parsed_file = importer.Import(input_files_[i]);
  518. if (parsed_file == NULL) return 1;
  519. parsed_files.push_back(parsed_file);
  520. // Enforce --disallow_services.
  521. if (disallow_services_ && parsed_file->service_count() > 0) {
  522. cerr << parsed_file->name() << ": This file contains services, but "
  523. "--disallow_services was used." << endl;
  524. return 1;
  525. }
  526. }
  527. // We construct a separate GeneratorContext for each output location. Note
  528. // that two code generators may output to the same location, in which case
  529. // they should share a single GeneratorContext so that OpenForInsert() works.
  530. typedef hash_map<string, GeneratorContextImpl*> GeneratorContextMap;
  531. GeneratorContextMap output_directories;
  532. // Generate output.
  533. if (mode_ == MODE_COMPILE) {
  534. for (int i = 0; i < output_directives_.size(); i++) {
  535. string output_location = output_directives_[i].output_location;
  536. if (!HasSuffixString(output_location, ".zip") &&
  537. !HasSuffixString(output_location, ".jar")) {
  538. AddTrailingSlash(&output_location);
  539. }
  540. GeneratorContextImpl** map_slot = &output_directories[output_location];
  541. if (*map_slot == NULL) {
  542. // First time we've seen this output location.
  543. *map_slot = new GeneratorContextImpl(parsed_files);
  544. }
  545. if (!GenerateOutput(parsed_files, output_directives_[i], *map_slot)) {
  546. STLDeleteValues(&output_directories);
  547. return 1;
  548. }
  549. }
  550. }
  551. // Write all output to disk.
  552. for (GeneratorContextMap::iterator iter = output_directories.begin();
  553. iter != output_directories.end(); ++iter) {
  554. const string& location = iter->first;
  555. GeneratorContextImpl* directory = iter->second;
  556. if (HasSuffixString(location, "/")) {
  557. if (!directory->WriteAllToDisk(location)) {
  558. STLDeleteValues(&output_directories);
  559. return 1;
  560. }
  561. } else {
  562. if (HasSuffixString(location, ".jar")) {
  563. directory->AddJarManifest();
  564. }
  565. if (!directory->WriteAllToZip(location)) {
  566. STLDeleteValues(&output_directories);
  567. return 1;
  568. }
  569. }
  570. }
  571. STLDeleteValues(&output_directories);
  572. if (!descriptor_set_name_.empty()) {
  573. if (!WriteDescriptorSet(parsed_files)) {
  574. return 1;
  575. }
  576. }
  577. if (mode_ == MODE_ENCODE || mode_ == MODE_DECODE) {
  578. if (codec_type_.empty()) {
  579. // HACK: Define an EmptyMessage type to use for decoding.
  580. DescriptorPool pool;
  581. FileDescriptorProto file;
  582. file.set_name("empty_message.proto");
  583. file.add_message_type()->set_name("EmptyMessage");
  584. GOOGLE_CHECK(pool.BuildFile(file) != NULL);
  585. codec_type_ = "EmptyMessage";
  586. if (!EncodeOrDecode(&pool)) {
  587. return 1;
  588. }
  589. } else {
  590. if (!EncodeOrDecode(importer.pool())) {
  591. return 1;
  592. }
  593. }
  594. }
  595. return 0;
  596. }
  597. void CommandLineInterface::Clear() {
  598. // Clear all members that are set by Run(). Note that we must not clear
  599. // members which are set by other methods before Run() is called.
  600. executable_name_.clear();
  601. proto_path_.clear();
  602. input_files_.clear();
  603. output_directives_.clear();
  604. codec_type_.clear();
  605. descriptor_set_name_.clear();
  606. mode_ = MODE_COMPILE;
  607. imports_in_descriptor_set_ = false;
  608. disallow_services_ = false;
  609. }
  610. bool CommandLineInterface::MakeInputsBeProtoPathRelative(
  611. DiskSourceTree* source_tree) {
  612. for (int i = 0; i < input_files_.size(); i++) {
  613. string virtual_file, shadowing_disk_file;
  614. switch (source_tree->DiskFileToVirtualFile(
  615. input_files_[i], &virtual_file, &shadowing_disk_file)) {
  616. case DiskSourceTree::SUCCESS:
  617. input_files_[i] = virtual_file;
  618. break;
  619. case DiskSourceTree::SHADOWED:
  620. cerr << input_files_[i] << ": Input is shadowed in the --proto_path "
  621. "by \"" << shadowing_disk_file << "\". Either use the latter "
  622. "file as your input or reorder the --proto_path so that the "
  623. "former file's location comes first." << endl;
  624. return false;
  625. case DiskSourceTree::CANNOT_OPEN:
  626. cerr << input_files_[i] << ": " << strerror(errno) << endl;
  627. return false;
  628. case DiskSourceTree::NO_MAPPING:
  629. // First check if the file exists at all.
  630. if (access(input_files_[i].c_str(), F_OK) < 0) {
  631. // File does not even exist.
  632. cerr << input_files_[i] << ": " << strerror(ENOENT) << endl;
  633. } else {
  634. cerr << input_files_[i] << ": File does not reside within any path "
  635. "specified using --proto_path (or -I). You must specify a "
  636. "--proto_path which encompasses this file. Note that the "
  637. "proto_path must be an exact prefix of the .proto file "
  638. "names -- protoc is too dumb to figure out when two paths "
  639. "(e.g. absolute and relative) are equivalent (it's harder "
  640. "than you think)." << endl;
  641. }
  642. return false;
  643. }
  644. }
  645. return true;
  646. }
  647. bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) {
  648. executable_name_ = argv[0];
  649. // Iterate through all arguments and parse them.
  650. for (int i = 1; i < argc; i++) {
  651. string name, value;
  652. if (ParseArgument(argv[i], &name, &value)) {
  653. // Returned true => Use the next argument as the flag value.
  654. if (i + 1 == argc || argv[i+1][0] == '-') {
  655. cerr << "Missing value for flag: " << name << endl;
  656. if (name == "--decode") {
  657. cerr << "To decode an unknown message, use --decode_raw." << endl;
  658. }
  659. return false;
  660. } else {
  661. ++i;
  662. value = argv[i];
  663. }
  664. }
  665. if (!InterpretArgument(name, value)) return false;
  666. }
  667. // If no --proto_path was given, use the current working directory.
  668. if (proto_path_.empty()) {
  669. proto_path_.push_back(make_pair<string, string>("", "."));
  670. }
  671. // Check some errror cases.
  672. bool decoding_raw = (mode_ == MODE_DECODE) && codec_type_.empty();
  673. if (decoding_raw && !input_files_.empty()) {
  674. cerr << "When using --decode_raw, no input files should be given." << endl;
  675. return false;
  676. } else if (!decoding_raw && input_files_.empty()) {
  677. cerr << "Missing input file." << endl;
  678. return false;
  679. }
  680. if (mode_ == MODE_COMPILE && output_directives_.empty() &&
  681. descriptor_set_name_.empty()) {
  682. cerr << "Missing output directives." << endl;
  683. return false;
  684. }
  685. if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) {
  686. cerr << "--include_imports only makes sense when combined with "
  687. "--descriptor_set_out." << endl;
  688. }
  689. return true;
  690. }
  691. bool CommandLineInterface::ParseArgument(const char* arg,
  692. string* name, string* value) {
  693. bool parsed_value = false;
  694. if (arg[0] != '-') {
  695. // Not a flag.
  696. name->clear();
  697. parsed_value = true;
  698. *value = arg;
  699. } else if (arg[1] == '-') {
  700. // Two dashes: Multi-character name, with '=' separating name and
  701. // value.
  702. const char* equals_pos = strchr(arg, '=');
  703. if (equals_pos != NULL) {
  704. *name = string(arg, equals_pos - arg);
  705. *value = equals_pos + 1;
  706. parsed_value = true;
  707. } else {
  708. *name = arg;
  709. }
  710. } else {
  711. // One dash: One-character name, all subsequent characters are the
  712. // value.
  713. if (arg[1] == '\0') {
  714. // arg is just "-". We treat this as an input file, except that at
  715. // present this will just lead to a "file not found" error.
  716. name->clear();
  717. *value = arg;
  718. parsed_value = true;
  719. } else {
  720. *name = string(arg, 2);
  721. *value = arg + 2;
  722. parsed_value = !value->empty();
  723. }
  724. }
  725. // Need to return true iff the next arg should be used as the value for this
  726. // one, false otherwise.
  727. if (parsed_value) {
  728. // We already parsed a value for this flag.
  729. return false;
  730. }
  731. if (*name == "-h" || *name == "--help" ||
  732. *name == "--disallow_services" ||
  733. *name == "--include_imports" ||
  734. *name == "--version" ||
  735. *name == "--decode_raw") {
  736. // HACK: These are the only flags that don't take a value.
  737. // They probably should not be hard-coded like this but for now it's
  738. // not worth doing better.
  739. return false;
  740. }
  741. // Next argument is the flag value.
  742. return true;
  743. }
  744. bool CommandLineInterface::InterpretArgument(const string& name,
  745. const string& value) {
  746. if (name.empty()) {
  747. // Not a flag. Just a filename.
  748. if (value.empty()) {
  749. cerr << "You seem to have passed an empty string as one of the "
  750. "arguments to " << executable_name_ << ". This is actually "
  751. "sort of hard to do. Congrats. Unfortunately it is not valid "
  752. "input so the program is going to die now." << endl;
  753. return false;
  754. }
  755. input_files_.push_back(value);
  756. } else if (name == "-I" || name == "--proto_path") {
  757. // Java's -classpath (and some other languages) delimits path components
  758. // with colons. Let's accept that syntax too just to make things more
  759. // intuitive.
  760. vector<string> parts;
  761. SplitStringUsing(value, kPathSeparator, &parts);
  762. for (int i = 0; i < parts.size(); i++) {
  763. string virtual_path;
  764. string disk_path;
  765. int equals_pos = parts[i].find_first_of('=');
  766. if (equals_pos == string::npos) {
  767. virtual_path = "";
  768. disk_path = parts[i];
  769. } else {
  770. virtual_path = parts[i].substr(0, equals_pos);
  771. disk_path = parts[i].substr(equals_pos + 1);
  772. }
  773. if (disk_path.empty()) {
  774. cerr << "--proto_path passed empty directory name. (Use \".\" for "
  775. "current directory.)" << endl;
  776. return false;
  777. }
  778. // Make sure disk path exists, warn otherwise.
  779. if (access(disk_path.c_str(), F_OK) < 0) {
  780. cerr << disk_path << ": warning: directory does not exist." << endl;
  781. }
  782. proto_path_.push_back(make_pair<string, string>(virtual_path, disk_path));
  783. }
  784. } else if (name == "-o" || name == "--descriptor_set_out") {
  785. if (!descriptor_set_name_.empty()) {
  786. cerr << name << " may only be passed once." << endl;
  787. return false;
  788. }
  789. if (value.empty()) {
  790. cerr << name << " requires a non-empty value." << endl;
  791. return false;
  792. }
  793. if (mode_ != MODE_COMPILE) {
  794. cerr << "Cannot use --encode or --decode and generate descriptors at the "
  795. "same time." << endl;
  796. return false;
  797. }
  798. descriptor_set_name_ = value;
  799. } else if (name == "--include_imports") {
  800. if (imports_in_descriptor_set_) {
  801. cerr << name << " may only be passed once." << endl;
  802. return false;
  803. }
  804. imports_in_descriptor_set_ = true;
  805. } else if (name == "-h" || name == "--help") {
  806. PrintHelpText();
  807. return false; // Exit without running compiler.
  808. } else if (name == "--version") {
  809. if (!version_info_.empty()) {
  810. cout << version_info_ << endl;
  811. }
  812. cout << "libprotoc "
  813. << protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION)
  814. << endl;
  815. return false; // Exit without running compiler.
  816. } else if (name == "--disallow_services") {
  817. disallow_services_ = true;
  818. } else if (name == "--encode" || name == "--decode" ||
  819. name == "--decode_raw") {
  820. if (mode_ != MODE_COMPILE) {
  821. cerr << "Only one of --encode and --decode can be specified." << endl;
  822. return false;
  823. }
  824. if (!output_directives_.empty() || !descriptor_set_name_.empty()) {
  825. cerr << "Cannot use " << name
  826. << " and generate code or descriptors at the same time." << endl;
  827. return false;
  828. }
  829. mode_ = (name == "--encode") ? MODE_ENCODE : MODE_DECODE;
  830. if (value.empty() && name != "--decode_raw") {
  831. cerr << "Type name for " << name << " cannot be blank." << endl;
  832. if (name == "--decode") {
  833. cerr << "To decode an unknown message, use --decode_raw." << endl;
  834. }
  835. return false;
  836. } else if (!value.empty() && name == "--decode_raw") {
  837. cerr << "--decode_raw does not take a parameter." << endl;
  838. return false;
  839. }
  840. codec_type_ = value;
  841. } else if (name == "--error_format") {
  842. if (value == "gcc") {
  843. error_format_ = ERROR_FORMAT_GCC;
  844. } else if (value == "msvs") {
  845. error_format_ = ERROR_FORMAT_MSVS;
  846. } else {
  847. cerr << "Unknown error format: " << value << endl;
  848. return false;
  849. }
  850. } else if (name == "--plugin") {
  851. if (plugin_prefix_.empty()) {
  852. cerr << "This compiler does not support plugins." << endl;
  853. return false;
  854. }
  855. string name;
  856. string path;
  857. string::size_type equals_pos = value.find_first_of('=');
  858. if (equals_pos == string::npos) {
  859. // Use the basename of the file.
  860. string::size_type slash_pos = value.find_last_of('/');
  861. if (slash_pos == string::npos) {
  862. name = value;
  863. } else {
  864. name = value.substr(slash_pos + 1);
  865. }
  866. path = value;
  867. } else {
  868. name = value.substr(0, equals_pos);
  869. path = value.substr(equals_pos + 1);
  870. }
  871. plugins_[name] = path;
  872. } else {
  873. // Some other flag. Look it up in the generators list.
  874. const GeneratorInfo* generator_info = FindOrNull(generators_, name);
  875. if (generator_info == NULL &&
  876. (plugin_prefix_.empty() || !HasSuffixString(name, "_out"))) {
  877. cerr << "Unknown flag: " << name << endl;
  878. return false;
  879. }
  880. // It's an output flag. Add it to the output directives.
  881. if (mode_ != MODE_COMPILE) {
  882. cerr << "Cannot use --encode or --decode and generate code at the "
  883. "same time." << endl;
  884. return false;
  885. }
  886. OutputDirective directive;
  887. directive.name = name;
  888. if (generator_info == NULL) {
  889. directive.generator = NULL;
  890. } else {
  891. directive.generator = generator_info->generator;
  892. }
  893. // Split value at ':' to separate the generator parameter from the
  894. // filename. However, avoid doing this if the colon is part of a valid
  895. // Windows-style absolute path.
  896. string::size_type colon_pos = value.find_first_of(':');
  897. if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) {
  898. directive.output_location = value;
  899. } else {
  900. directive.parameter = value.substr(0, colon_pos);
  901. directive.output_location = value.substr(colon_pos + 1);
  902. }
  903. output_directives_.push_back(directive);
  904. }
  905. return true;
  906. }
  907. void CommandLineInterface::PrintHelpText() {
  908. // Sorry for indentation here; line wrapping would be uglier.
  909. cerr <<
  910. "Usage: " << executable_name_ << " [OPTION] PROTO_FILES\n"
  911. "Parse PROTO_FILES and generate output based on the options given:\n"
  912. " -IPATH, --proto_path=PATH Specify the directory in which to search for\n"
  913. " imports. May be specified multiple times;\n"
  914. " directories will be searched in order. If not\n"
  915. " given, the current working directory is used.\n"
  916. " --version Show version info and exit.\n"
  917. " -h, --help Show this text and exit.\n"
  918. " --encode=MESSAGE_TYPE Read a text-format message of the given type\n"
  919. " from standard input and write it in binary\n"
  920. " to standard output. The message type must\n"
  921. " be defined in PROTO_FILES or their imports.\n"
  922. " --decode=MESSAGE_TYPE Read a binary message of the given type from\n"
  923. " standard input and write it in text format\n"
  924. " to standard output. The message type must\n"
  925. " be defined in PROTO_FILES or their imports.\n"
  926. " --decode_raw Read an arbitrary protocol message from\n"
  927. " standard input and write the raw tag/value\n"
  928. " pairs in text format to standard output. No\n"
  929. " PROTO_FILES should be given when using this\n"
  930. " flag.\n"
  931. " -oFILE, Writes a FileDescriptorSet (a protocol buffer,\n"
  932. " --descriptor_set_out=FILE defined in descriptor.proto) containing all of\n"
  933. " the input files to FILE.\n"
  934. " --include_imports When using --descriptor_set_out, also include\n"
  935. " all dependencies of the input files in the\n"
  936. " set, so that the set is self-contained.\n"
  937. " --error_format=FORMAT Set the format in which to print errors.\n"
  938. " FORMAT may be 'gcc' (the default) or 'msvs'\n"
  939. " (Microsoft Visual Studio format)." << endl;
  940. if (!plugin_prefix_.empty()) {
  941. cerr <<
  942. " --plugin=EXECUTABLE Specifies a plugin executable to use.\n"
  943. " Normally, protoc searches the PATH for\n"
  944. " plugins, but you may specify additional\n"
  945. " executables not in the path using this flag.\n"
  946. " Additionally, EXECUTABLE may be of the form\n"
  947. " NAME=PATH, in which case the given plugin name\n"
  948. " is mapped to the given executable even if\n"
  949. " the executable's own name differs." << endl;
  950. }
  951. for (GeneratorMap::iterator iter = generators_.begin();
  952. iter != generators_.end(); ++iter) {
  953. // FIXME(kenton): If the text is long enough it will wrap, which is ugly,
  954. // but fixing this nicely (e.g. splitting on spaces) is probably more
  955. // trouble than it's worth.
  956. cerr << " " << iter->first << "=OUT_DIR "
  957. << string(19 - iter->first.size(), ' ') // Spaces for alignment.
  958. << iter->second.help_text << endl;
  959. }
  960. }
  961. bool CommandLineInterface::GenerateOutput(
  962. const vector<const FileDescriptor*>& parsed_files,
  963. const OutputDirective& output_directive,
  964. GeneratorContext* generator_context) {
  965. // Call the generator.
  966. string error;
  967. if (output_directive.generator == NULL) {
  968. // This is a plugin.
  969. GOOGLE_CHECK(HasPrefixString(output_directive.name, "--") &&
  970. HasSuffixString(output_directive.name, "_out"))
  971. << "Bad name for plugin generator: " << output_directive.name;
  972. // Strip the "--" and "_out" and add the plugin prefix.
  973. string plugin_name = plugin_prefix_ + "gen-" +
  974. output_directive.name.substr(2, output_directive.name.size() - 6);
  975. if (!GeneratePluginOutput(parsed_files, plugin_name,
  976. output_directive.parameter,
  977. generator_context, &error)) {
  978. cerr << output_directive.name << ": " << error << endl;
  979. return false;
  980. }
  981. } else {
  982. // Regular generator.
  983. for (int i = 0; i < parsed_files.size(); i++) {
  984. if (!output_directive.generator->Generate(
  985. parsed_files[i], output_directive.parameter,
  986. generator_context, &error)) {
  987. // Generator returned an error.
  988. cerr << output_directive.name << ": " << parsed_files[i]->name() << ": "
  989. << error << endl;
  990. return false;
  991. }
  992. }
  993. }
  994. return true;
  995. }
  996. bool CommandLineInterface::GeneratePluginOutput(
  997. const vector<const FileDescriptor*>& parsed_files,
  998. const string& plugin_name,
  999. const string& parameter,
  1000. GeneratorContext* generator_context,
  1001. string* error) {
  1002. CodeGeneratorRequest request;
  1003. CodeGeneratorResponse response;
  1004. // Build the request.
  1005. if (!parameter.empty()) {
  1006. request.set_parameter(parameter);
  1007. }
  1008. set<const FileDescriptor*> already_seen;
  1009. for (int i = 0; i < parsed_files.size(); i++) {
  1010. request.add_file_to_generate(parsed_files[i]->name());
  1011. GetTransitiveDependencies(parsed_files[i], &already_seen,
  1012. request.mutable_proto_file());
  1013. }
  1014. // Invoke the plugin.
  1015. Subprocess subprocess;
  1016. if (plugins_.count(plugin_name) > 0) {
  1017. subprocess.Start(plugins_[plugin_name], Subprocess::EXACT_NAME);
  1018. } else {
  1019. subprocess.Start(plugin_name, Subprocess::SEARCH_PATH);
  1020. }
  1021. string communicate_error;
  1022. if (!subprocess.Communicate(request, &response, &communicate_error)) {
  1023. *error = strings::Substitute("$0: $1", plugin_name, communicate_error);
  1024. return false;
  1025. }
  1026. // Write the files. We do this even if there was a generator error in order
  1027. // to match the behavior of a compiled-in generator.
  1028. scoped_ptr<io::ZeroCopyOutputStream> current_output;
  1029. for (int i = 0; i < response.file_size(); i++) {
  1030. const CodeGeneratorResponse::File& output_file = response.file(i);
  1031. if (!output_file.insertion_point().empty()) {
  1032. // Open a file for insert.
  1033. // We reset current_output to NULL first so that the old file is closed
  1034. // before the new one is opened.
  1035. current_output.reset();
  1036. current_output.reset(generator_context->OpenForInsert(
  1037. output_file.name(), output_file.insertion_point()));
  1038. } else if (!output_file.name().empty()) {
  1039. // Starting a new file. Open it.
  1040. // We reset current_output to NULL first so that the old file is closed
  1041. // before the new one is opened.
  1042. current_output.reset();
  1043. current_output.reset(generator_context->Open(output_file.name()));
  1044. } else if (current_output == NULL) {
  1045. *error = strings::Substitute(
  1046. "$0: First file chunk returned by plugin did not specify a file name.",
  1047. plugin_name);
  1048. return false;
  1049. }
  1050. // Use CodedOutputStream for convenience; otherwise we'd need to provide
  1051. // our own buffer-copying loop.
  1052. io::CodedOutputStream writer(current_output.get());
  1053. writer.WriteString(output_file.content());
  1054. }
  1055. // Check for errors.
  1056. if (!response.error().empty()) {
  1057. // Generator returned an error.
  1058. *error = response.error();
  1059. return false;
  1060. }
  1061. return true;
  1062. }
  1063. bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) {
  1064. // Look up the type.
  1065. const Descriptor* type = pool->FindMessageTypeByName(codec_type_);
  1066. if (type == NULL) {
  1067. cerr << "Type not defined: " << codec_type_ << endl;
  1068. return false;
  1069. }
  1070. DynamicMessageFactory dynamic_factory(pool);
  1071. scoped_ptr<Message> message(dynamic_factory.GetPrototype(type)->New());
  1072. if (mode_ == MODE_ENCODE) {
  1073. SetFdToTextMode(STDIN_FILENO);
  1074. SetFdToBinaryMode(STDOUT_FILENO);
  1075. } else {
  1076. SetFdToBinaryMode(STDIN_FILENO);
  1077. SetFdToTextMode(STDOUT_FILENO);
  1078. }
  1079. io::FileInputStream in(STDIN_FILENO);
  1080. io::FileOutputStream out(STDOUT_FILENO);
  1081. if (mode_ == MODE_ENCODE) {
  1082. // Input is text.
  1083. ErrorPrinter error_collector(error_format_);
  1084. TextFormat::Parser parser;
  1085. parser.RecordErrorsTo(&error_collector);
  1086. parser.AllowPartialMessage(true);
  1087. if (!parser.Parse(&in, message.get())) {
  1088. cerr << "Failed to parse input." << endl;
  1089. return false;
  1090. }
  1091. } else {
  1092. // Input is binary.
  1093. if (!message->ParsePartialFromZeroCopyStream(&in)) {
  1094. cerr << "Failed to parse input." << endl;
  1095. return false;
  1096. }
  1097. }
  1098. if (!message->IsInitialized()) {
  1099. cerr << "warning: Input message is missing required fields: "
  1100. << message->InitializationErrorString() << endl;
  1101. }
  1102. if (mode_ == MODE_ENCODE) {
  1103. // Output is binary.
  1104. if (!message->SerializePartialToZeroCopyStream(&out)) {
  1105. cerr << "output: I/O error." << endl;
  1106. return false;
  1107. }
  1108. } else {
  1109. // Output is text.
  1110. if (!TextFormat::Print(*message, &out)) {
  1111. cerr << "output: I/O error." << endl;
  1112. return false;
  1113. }
  1114. }
  1115. return true;
  1116. }
  1117. bool CommandLineInterface::WriteDescriptorSet(
  1118. const vector<const FileDescriptor*> parsed_files) {
  1119. FileDescriptorSet file_set;
  1120. if (imports_in_descriptor_set_) {
  1121. set<const FileDescriptor*> already_seen;
  1122. for (int i = 0; i < parsed_files.size(); i++) {
  1123. GetTransitiveDependencies(
  1124. parsed_files[i], &already_seen, file_set.mutable_file());
  1125. }
  1126. } else {
  1127. for (int i = 0; i < parsed_files.size(); i++) {
  1128. parsed_files[i]->CopyTo(file_set.add_file());
  1129. }
  1130. }
  1131. int fd;
  1132. do {
  1133. fd = open(descriptor_set_name_.c_str(),
  1134. O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
  1135. } while (fd < 0 && errno == EINTR);
  1136. if (fd < 0) {
  1137. perror(descriptor_set_name_.c_str());
  1138. return false;
  1139. }
  1140. io::FileOutputStream out(fd);
  1141. if (!file_set.SerializeToZeroCopyStream(&out)) {
  1142. cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) << endl;
  1143. out.Close();
  1144. return false;
  1145. }
  1146. if (!out.Close()) {
  1147. cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) << endl;
  1148. return false;
  1149. }
  1150. return true;
  1151. }
  1152. void CommandLineInterface::GetTransitiveDependencies(
  1153. const FileDescriptor* file,
  1154. set<const FileDescriptor*>* already_seen,
  1155. RepeatedPtrField<FileDescriptorProto>* output) {
  1156. if (!already_seen->insert(file).second) {
  1157. // Already saw this file. Skip.
  1158. return;
  1159. }
  1160. // Add all dependencies.
  1161. for (int i = 0; i < file->dependency_count(); i++) {
  1162. GetTransitiveDependencies(file->dependency(i), already_seen, output);
  1163. }
  1164. // Add this file.
  1165. file->CopyTo(output->Add());
  1166. }
  1167. } // namespace compiler
  1168. } // namespace protobuf
  1169. } // namespace google