PageRenderTime 78ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/src/google/protobuf/compiler/command_line_interface.cc

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