~ubuntu-branches/ubuntu/oneiric/protobuf/oneiric

« back to all changes in this revision

Viewing changes to src/google/protobuf/compiler/command_line_interface.cc

  • Committer: Bazaar Package Importer
  • Author(s): Iustin Pop
  • Date: 2008-08-03 11:01:44 UTC
  • Revision ID: james.westby@ubuntu.com-20080803110144-uyiw41bf1m2oe17t
Tags: upstream-2.0.0~b
ImportĀ upstreamĀ versionĀ 2.0.0~b

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Protocol Buffers - Google's data interchange format
 
2
// Copyright 2008 Google Inc.
 
3
// http://code.google.com/p/protobuf/
 
4
//
 
5
// Licensed under the Apache License, Version 2.0 (the "License");
 
6
// you may not use this file except in compliance with the License.
 
7
// You may obtain a copy of the License at
 
8
//
 
9
//      http://www.apache.org/licenses/LICENSE-2.0
 
10
//
 
11
// Unless required by applicable law or agreed to in writing, software
 
12
// distributed under the License is distributed on an "AS IS" BASIS,
 
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
14
// See the License for the specific language governing permissions and
 
15
// limitations under the License.
 
16
 
 
17
// Author: kenton@google.com (Kenton Varda)
 
18
//  Based on original Protocol Buffers design by
 
19
//  Sanjay Ghemawat, Jeff Dean, and others.
 
20
 
 
21
#include <sys/types.h>
 
22
#include <sys/stat.h>
 
23
#include <fcntl.h>
 
24
#ifdef _MSC_VER
 
25
#include <io.h>
 
26
#include <direct.h>
 
27
#else
 
28
#include <unistd.h>
 
29
#endif
 
30
#include <errno.h>
 
31
#include <iostream>
 
32
 
 
33
#include <google/protobuf/compiler/command_line_interface.h>
 
34
#include <google/protobuf/compiler/importer.h>
 
35
#include <google/protobuf/compiler/code_generator.h>
 
36
#include <google/protobuf/descriptor.h>
 
37
#include <google/protobuf/io/zero_copy_stream_impl.h>
 
38
#include <google/protobuf/stubs/common.h>
 
39
#include <google/protobuf/stubs/strutil.h>
 
40
 
 
41
 
 
42
namespace google {
 
43
namespace protobuf {
 
44
namespace compiler {
 
45
 
 
46
#if defined(_WIN32)
 
47
#define mkdir(name, mode) mkdir(name)
 
48
#ifndef W_OK
 
49
#define W_OK 02  // not defined by MSVC for whatever reason
 
50
#endif
 
51
#ifndef F_OK
 
52
#define F_OK 00  // not defined by MSVC for whatever reason
 
53
#endif
 
54
#endif
 
55
 
 
56
#ifndef O_BINARY
 
57
#ifdef _O_BINARY
 
58
#define O_BINARY _O_BINARY
 
59
#else
 
60
#define O_BINARY 0     // If this isn't defined, the platform doesn't need it.
 
61
#endif
 
62
#endif
 
63
 
 
64
namespace {
 
65
#if defined(_WIN32) && !defined(__CYGWIN__)
 
66
static const char* kPathSeparator = ";";
 
67
#else
 
68
static const char* kPathSeparator = ":";
 
69
#endif
 
70
}  // namespace
 
71
 
 
72
// A MultiFileErrorCollector that prints errors to stderr.
 
73
class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector {
 
74
 public:
 
75
  ErrorPrinter() {}
 
76
  ~ErrorPrinter() {}
 
77
 
 
78
  // implements MultiFileErrorCollector ------------------------------
 
79
  void AddError(const string& filename, int line, int column,
 
80
                const string& message) {
 
81
    // Users typically expect 1-based line/column numbers, so we add 1
 
82
    // to each here.
 
83
    cerr << filename;
 
84
    if (line != -1) {
 
85
      cerr << ":" << (line + 1) << ":" << (column + 1);
 
86
    }
 
87
    cerr << ": " << message << endl;
 
88
  }
 
89
};
 
90
 
 
91
// -------------------------------------------------------------------
 
92
 
 
93
// An OutputDirectory implementation that writes to disk.
 
94
class CommandLineInterface::DiskOutputDirectory : public OutputDirectory {
 
95
 public:
 
96
  DiskOutputDirectory(const string& root);
 
97
  ~DiskOutputDirectory();
 
98
 
 
99
  bool VerifyExistence();
 
100
 
 
101
  inline bool had_error() { return had_error_; }
 
102
  inline void set_had_error(bool value) { had_error_ = value; }
 
103
 
 
104
  // implements OutputDirectory --------------------------------------
 
105
  io::ZeroCopyOutputStream* Open(const string& filename);
 
106
 
 
107
 private:
 
108
  string root_;
 
109
  bool had_error_;
 
110
};
 
111
 
 
112
// A FileOutputStream that checks for errors in the destructor and reports
 
113
// them.  We extend FileOutputStream via wrapping rather than inheritance
 
114
// for two reasons:
 
115
// 1) Implementation inheritance is evil.
 
116
// 2) We need to close the file descriptor *after* the FileOutputStream's
 
117
//    destructor is run to make sure it flushes the file contents.
 
118
class CommandLineInterface::ErrorReportingFileOutput
 
119
    : public io::ZeroCopyOutputStream {
 
120
 public:
 
121
  ErrorReportingFileOutput(int file_descriptor,
 
122
                           const string& filename,
 
123
                           DiskOutputDirectory* directory);
 
124
  ~ErrorReportingFileOutput();
 
125
 
 
126
  // implements ZeroCopyOutputStream ---------------------------------
 
127
  bool Next(void** data, int* size) { return file_stream_->Next(data, size); }
 
128
  void BackUp(int count)            {        file_stream_->BackUp(count);    }
 
129
  int64 ByteCount() const           { return file_stream_->ByteCount();      }
 
130
 
 
131
 private:
 
132
  scoped_ptr<io::FileOutputStream> file_stream_;
 
133
  int file_descriptor_;
 
134
  string filename_;
 
135
  DiskOutputDirectory* directory_;
 
136
};
 
137
 
 
138
// -------------------------------------------------------------------
 
139
 
 
140
CommandLineInterface::DiskOutputDirectory::DiskOutputDirectory(
 
141
    const string& root)
 
142
  : root_(root), had_error_(false) {
 
143
  // Add a '/' to the end if it doesn't already have one.  But don't add a
 
144
  // '/' to an empty string since this probably means the current directory.
 
145
  if (!root_.empty() && root[root_.size() - 1] != '/') {
 
146
    root_ += '/';
 
147
  }
 
148
}
 
149
 
 
150
CommandLineInterface::DiskOutputDirectory::~DiskOutputDirectory() {
 
151
}
 
152
 
 
153
bool CommandLineInterface::DiskOutputDirectory::VerifyExistence() {
 
154
  if (!root_.empty()) {
 
155
    // Make sure the directory exists.  If it isn't a directory, this will fail
 
156
    // because we added a '/' to the end of the name in the constructor.
 
157
    if (access(root_.c_str(), W_OK) == -1) {
 
158
      cerr << root_ << ": " << strerror(errno) << endl;
 
159
      return false;
 
160
    }
 
161
  }
 
162
 
 
163
  return true;
 
164
}
 
165
 
 
166
io::ZeroCopyOutputStream* CommandLineInterface::DiskOutputDirectory::Open(
 
167
    const string& filename) {
 
168
  // Recursively create parent directories to the output file.
 
169
  vector<string> parts;
 
170
  SplitStringUsing(filename, "/", &parts);
 
171
  string path_so_far = root_;
 
172
  for (int i = 0; i < parts.size() - 1; i++) {
 
173
    path_so_far += parts[i];
 
174
    if (mkdir(path_so_far.c_str(), 0777) != 0) {
 
175
      if (errno != EEXIST) {
 
176
        cerr << filename << ": while trying to create directory "
 
177
             << path_so_far << ": " << strerror(errno) << endl;
 
178
        had_error_ = true;
 
179
        // Return a dummy stream.
 
180
        return new io::ArrayOutputStream(NULL, 0);
 
181
      }
 
182
    }
 
183
    path_so_far += '/';
 
184
  }
 
185
 
 
186
  // Create the output file.
 
187
  int file_descriptor;
 
188
  do {
 
189
    file_descriptor =
 
190
      open((root_ + filename).c_str(),
 
191
           O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0777);
 
192
  } while (file_descriptor < 0 && errno == EINTR);
 
193
 
 
194
  if (file_descriptor < 0) {
 
195
    // Failed to open.
 
196
    cerr << filename << ": " << strerror(errno) << endl;
 
197
    had_error_ = true;
 
198
    // Return a dummy stream.
 
199
    return new io::ArrayOutputStream(NULL, 0);
 
200
  }
 
201
 
 
202
  return new ErrorReportingFileOutput(file_descriptor, filename, this);
 
203
}
 
204
 
 
205
CommandLineInterface::ErrorReportingFileOutput::ErrorReportingFileOutput(
 
206
    int file_descriptor,
 
207
    const string& filename,
 
208
    DiskOutputDirectory* directory)
 
209
  : file_stream_(new io::FileOutputStream(file_descriptor)),
 
210
    file_descriptor_(file_descriptor),
 
211
    filename_(filename),
 
212
    directory_(directory) {}
 
213
 
 
214
CommandLineInterface::ErrorReportingFileOutput::~ErrorReportingFileOutput() {
 
215
  // Check if we had any errors while writing.
 
216
  if (file_stream_->GetErrno() != 0) {
 
217
    cerr << filename_ << ": " << strerror(file_stream_->GetErrno()) << endl;
 
218
    directory_->set_had_error(true);
 
219
  }
 
220
 
 
221
  // Close the file stream.
 
222
  if (!file_stream_->Close()) {
 
223
    cerr << filename_ << ": " << strerror(file_stream_->GetErrno()) << endl;
 
224
    directory_->set_had_error(true);
 
225
  }
 
226
}
 
227
 
 
228
// ===================================================================
 
229
 
 
230
CommandLineInterface::CommandLineInterface()
 
231
  : disallow_services_(false),
 
232
    inputs_are_proto_path_relative_(false) {}
 
233
CommandLineInterface::~CommandLineInterface() {}
 
234
 
 
235
void CommandLineInterface::RegisterGenerator(const string& flag_name,
 
236
                                             CodeGenerator* generator,
 
237
                                             const string& help_text) {
 
238
  GeneratorInfo info;
 
239
  info.generator = generator;
 
240
  info.help_text = help_text;
 
241
  generators_[flag_name] = info;
 
242
}
 
243
 
 
244
int CommandLineInterface::Run(int argc, const char* const argv[]) {
 
245
  Clear();
 
246
  if (!ParseArguments(argc, argv)) return -1;
 
247
 
 
248
  // Set up the source tree.
 
249
  DiskSourceTree source_tree;
 
250
  for (int i = 0; i < proto_path_.size(); i++) {
 
251
    source_tree.MapPath(proto_path_[i].first, proto_path_[i].second);
 
252
  }
 
253
 
 
254
  // Map input files to virtual paths if necessary.
 
255
  if (!inputs_are_proto_path_relative_) {
 
256
    if (!MakeInputsBeProtoPathRelative(&source_tree)) {
 
257
      return -1;
 
258
    }
 
259
  }
 
260
 
 
261
  // Allocate the Importer.
 
262
  ErrorPrinter error_collector;
 
263
  DescriptorPool pool;
 
264
  Importer importer(&source_tree, &error_collector);
 
265
 
 
266
  // Parse each file and generate output.
 
267
  for (int i = 0; i < input_files_.size(); i++) {
 
268
    // Import the file.
 
269
    const FileDescriptor* parsed_file = importer.Import(input_files_[i]);
 
270
    if (parsed_file == NULL) return -1;
 
271
 
 
272
    // Enforce --disallow_services.
 
273
    if (disallow_services_ && parsed_file->service_count() > 0) {
 
274
      cerr << parsed_file->name() << ": This file contains services, but "
 
275
              "--disallow_services was used." << endl;
 
276
      return -1;
 
277
    }
 
278
 
 
279
    // Generate output files.
 
280
    for (int i = 0; i < output_directives_.size(); i++) {
 
281
      if (!GenerateOutput(parsed_file, output_directives_[i])) {
 
282
        return -1;
 
283
      }
 
284
    }
 
285
  }
 
286
 
 
287
  return 0;
 
288
}
 
289
 
 
290
void CommandLineInterface::Clear() {
 
291
  proto_path_.clear();
 
292
  input_files_.clear();
 
293
  output_directives_.clear();
 
294
}
 
295
 
 
296
bool CommandLineInterface::MakeInputsBeProtoPathRelative(
 
297
    DiskSourceTree* source_tree) {
 
298
  for (int i = 0; i < input_files_.size(); i++) {
 
299
    string virtual_file, shadowing_disk_file;
 
300
    switch (source_tree->DiskFileToVirtualFile(
 
301
        input_files_[i], &virtual_file, &shadowing_disk_file)) {
 
302
      case DiskSourceTree::SUCCESS:
 
303
        input_files_[i] = virtual_file;
 
304
        break;
 
305
      case DiskSourceTree::SHADOWED:
 
306
        cerr << input_files_[i] << ": Input is shadowed in the --proto_path "
 
307
                "by \"" << shadowing_disk_file << "\".  Either use the latter "
 
308
                "file as your input or reorder the --proto_path so that the "
 
309
                "former file's location comes first." << endl;
 
310
        return false;
 
311
      case DiskSourceTree::CANNOT_OPEN:
 
312
        cerr << input_files_[i] << ": " << strerror(errno) << endl;
 
313
        return false;
 
314
      case DiskSourceTree::NO_MAPPING:
 
315
        // First check if the file exists at all.
 
316
        if (access(input_files_[i].c_str(), F_OK) < 0) {
 
317
          // File does not even exist.
 
318
          cerr << input_files_[i] << ": " << strerror(ENOENT) << endl;
 
319
        } else {
 
320
          cerr << input_files_[i] << ": File does not reside within any path "
 
321
                  "specified using --proto_path (or -I).  You must specify a "
 
322
                  "--proto_path which encompasses this file." << endl;
 
323
        }
 
324
        return false;
 
325
    }
 
326
  }
 
327
 
 
328
  return true;
 
329
}
 
330
 
 
331
bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) {
 
332
  executable_name_ = argv[0];
 
333
 
 
334
  // Iterate through all arguments and parse them.
 
335
  for (int i = 1; i < argc; i++) {
 
336
    string name, value;
 
337
 
 
338
    if (ParseArgument(argv[i], &name, &value)) {
 
339
      // Retured true => Use the next argument as the flag value.
 
340
      if (i + 1 == argc || argv[i+1][0] == '-') {
 
341
        cerr << "Missing value for flag: " << name << endl;
 
342
        return false;
 
343
      } else {
 
344
        ++i;
 
345
        value = argv[i];
 
346
      }
 
347
    }
 
348
 
 
349
    if (!InterpretArgument(name, value)) return false;
 
350
  }
 
351
 
 
352
  // If no --proto_path was given, use the current working directory.
 
353
  if (proto_path_.empty()) {
 
354
    proto_path_.push_back(make_pair("", "."));
 
355
  }
 
356
 
 
357
  // Check some errror cases.
 
358
  if (input_files_.empty()) {
 
359
    cerr << "Missing input file." << endl;
 
360
    return false;
 
361
  }
 
362
  if (output_directives_.empty()) {
 
363
    cerr << "Missing output directives." << endl;
 
364
    return false;
 
365
  }
 
366
 
 
367
  return true;
 
368
}
 
369
 
 
370
bool CommandLineInterface::ParseArgument(const char* arg,
 
371
                                         string* name, string* value) {
 
372
  bool parsed_value = false;
 
373
 
 
374
  if (arg[0] != '-') {
 
375
    // Not a flag.
 
376
    name->clear();
 
377
    parsed_value = true;
 
378
    *value = arg;
 
379
  } else if (arg[1] == '-') {
 
380
    // Two dashes:  Multi-character name, with '=' separating name and
 
381
    //   value.
 
382
    const char* equals_pos = strchr(arg, '=');
 
383
    if (equals_pos != NULL) {
 
384
      *name = string(arg, equals_pos - arg);
 
385
      *value = equals_pos + 1;
 
386
      parsed_value = true;
 
387
    } else {
 
388
      *name = arg;
 
389
    }
 
390
  } else {
 
391
    // One dash:  One-character name, all subsequent characters are the
 
392
    //   value.
 
393
    if (arg[1] == '\0') {
 
394
      // arg is just "-".  We treat this as an input file, except that at
 
395
      // present this will just lead to a "file not found" error.
 
396
      name->clear();
 
397
      *value = arg;
 
398
      parsed_value = true;
 
399
    } else {
 
400
      *name = string(arg, 2);
 
401
      *value = arg + 2;
 
402
      parsed_value = !value->empty();
 
403
    }
 
404
  }
 
405
 
 
406
  // Need to return true iff the next arg should be used as the value for this
 
407
  // one, false otherwise.
 
408
 
 
409
  if (parsed_value) {
 
410
    // We already parsed a value for this flag.
 
411
    return false;
 
412
  }
 
413
 
 
414
  if (*name == "-h" || *name == "--help" ||
 
415
      *name == "--disallow_services" ||
 
416
      *name == "--version") {
 
417
    // HACK:  These are the only flags that don't take a value.
 
418
    //   They probably should not be hard-coded like this but for now it's
 
419
    //   not worth doing better.
 
420
    return false;
 
421
  }
 
422
 
 
423
  // Next argument is the flag value.
 
424
  return true;
 
425
}
 
426
 
 
427
bool CommandLineInterface::InterpretArgument(const string& name,
 
428
                                             const string& value) {
 
429
  if (name.empty()) {
 
430
    // Not a flag.  Just a filename.
 
431
    if (value.empty()) {
 
432
      cerr << "You seem to have passed an empty string as one of the "
 
433
              "arguments to " << executable_name_ << ".  This is actually "
 
434
              "sort of hard to do.  Congrats.  Unfortunately it is not valid "
 
435
              "input so the program is going to die now." << endl;
 
436
      return false;
 
437
    }
 
438
 
 
439
    input_files_.push_back(value);
 
440
 
 
441
  } else if (name == "-I" || name == "--proto_path") {
 
442
    // Java's -classpath (and some other languages) delimits path components
 
443
    // with colons.  Let's accept that syntax too just to make things more
 
444
    // intuitive.
 
445
    vector<string> parts;
 
446
    SplitStringUsing(value, kPathSeparator, &parts);
 
447
 
 
448
    for (int i = 0; i < parts.size(); i++) {
 
449
      string virtual_path;
 
450
      string disk_path;
 
451
 
 
452
      int equals_pos = parts[i].find_first_of('=');
 
453
      if (equals_pos == string::npos) {
 
454
        virtual_path = "";
 
455
        disk_path = parts[i];
 
456
      } else {
 
457
        virtual_path = parts[i].substr(0, equals_pos);
 
458
        disk_path = parts[i].substr(equals_pos + 1);
 
459
      }
 
460
 
 
461
      if (disk_path.empty()) {
 
462
        cerr << "--proto_path passed empty directory name.  (Use \".\" for "
 
463
                "current directory.)" << endl;
 
464
        return false;
 
465
      }
 
466
 
 
467
      // Make sure disk path exists, warn otherwise.
 
468
      if (access(disk_path.c_str(), F_OK) < 0) {
 
469
        cerr << disk_path << ": warning: directory does not exist." << endl;
 
470
      }
 
471
 
 
472
      proto_path_.push_back(make_pair(virtual_path, disk_path));
 
473
    }
 
474
 
 
475
  } else if (name == "-h" || name == "--help") {
 
476
    PrintHelpText();
 
477
    return false;  // Exit without running compiler.
 
478
 
 
479
  } else if (name == "--version") {
 
480
    if (!version_info_.empty()) {
 
481
      cout << version_info_ << endl;
 
482
    }
 
483
    cout << "libprotoc "
 
484
         << protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION)
 
485
         << endl;
 
486
    return false;  // Exit without running compiler.
 
487
 
 
488
  } else if (name == "--disallow_services") {
 
489
    disallow_services_ = true;
 
490
 
 
491
  } else {
 
492
    // Some other flag.  Look it up in the generators list.
 
493
    GeneratorMap::const_iterator iter = generators_.find(name);
 
494
    if (iter == generators_.end()) {
 
495
      cerr << "Unknown flag: " << name << endl;
 
496
      return false;
 
497
    }
 
498
 
 
499
    // It's an output flag.  Add it to the output directives.
 
500
    OutputDirective directive;
 
501
    directive.name = name;
 
502
    directive.generator = iter->second.generator;
 
503
 
 
504
    // Split value at ':' to separate the generator parameter from the
 
505
    // filename.
 
506
    vector<string> parts;
 
507
    SplitStringUsing(value, ":", &parts);
 
508
 
 
509
    if (parts.size() == 1) {
 
510
      directive.output_location = parts[0];
 
511
    } else if (parts.size() == 2) {
 
512
      directive.parameter = parts[0];
 
513
      directive.output_location = parts[1];
 
514
    } else {
 
515
      cerr << "Invalid value for flag " << name << "." << endl;
 
516
      return false;
 
517
    }
 
518
 
 
519
    output_directives_.push_back(directive);
 
520
  }
 
521
 
 
522
  return true;
 
523
}
 
524
 
 
525
void CommandLineInterface::PrintHelpText() {
 
526
  // Sorry for indentation here; line wrapping would be uglier.
 
527
  cerr <<
 
528
"Usage: " << executable_name_ << " [OPTION] PROTO_FILE\n"
 
529
"Parse PROTO_FILE and generate output based on the options given:\n"
 
530
"  -IPATH, --proto_path=PATH   Specify the directory in which to search for\n"
 
531
"                              imports.  May be specified multiple times;\n"
 
532
"                              directories will be searched in order.  If not\n"
 
533
"                              given, the current working directory is used.\n"
 
534
"  --version                   Show version info and exit.\n"
 
535
"  -h, --help                  Show this text and exit." << endl;
 
536
 
 
537
  for (GeneratorMap::iterator iter = generators_.begin();
 
538
       iter != generators_.end(); ++iter) {
 
539
    // FIXME(kenton):  If the text is long enough it will wrap, which is ugly,
 
540
    //   but fixing this nicely (e.g. splitting on spaces) is probably more
 
541
    //   trouble than it's worth.
 
542
    cerr << "  " << iter->first << "=OUT_DIR "
 
543
         << string(19 - iter->first.size(), ' ')  // Spaces for alignment.
 
544
         << iter->second.help_text << endl;
 
545
  }
 
546
}
 
547
 
 
548
bool CommandLineInterface::GenerateOutput(
 
549
    const FileDescriptor* parsed_file,
 
550
    const OutputDirective& output_directive) {
 
551
  // Create the output directory.
 
552
  DiskOutputDirectory output_directory(output_directive.output_location);
 
553
  if (!output_directory.VerifyExistence()) {
 
554
    return false;
 
555
  }
 
556
 
 
557
  // Opened successfully.  Write it.
 
558
 
 
559
  // Call the generator.
 
560
  string error;
 
561
  if (!output_directive.generator->Generate(
 
562
      parsed_file, output_directive.parameter, &output_directory, &error)) {
 
563
    // Generator returned an error.
 
564
    cerr << output_directive.name << ": " << error << endl;
 
565
    return false;
 
566
  }
 
567
 
 
568
  // Check for write errors.
 
569
  if (output_directory.had_error()) {
 
570
    return false;
 
571
  }
 
572
 
 
573
  return true;
 
574
}
 
575
 
 
576
 
 
577
}  // namespace compiler
 
578
}  // namespace protobuf
 
579
}  // namespace google