Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1095)

Side by Side Diff: third_party/protobuf/objectivec/DevTools/pddm.py

Issue 1842653006: Update //third_party/protobuf to version 3. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: merge Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #! /usr/bin/python
2 #
3 # Protocol Buffers - Google's data interchange format
4 # Copyright 2015 Google Inc. All rights reserved.
5 # https://developers.google.com/protocol-buffers/
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following disclaimer
15 # in the documentation and/or other materials provided with the
16 # distribution.
17 # * Neither the name of Google Inc. nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 """PDDM - Poor Developers' Debug-able Macros
34
35 A simple markup that can be added in comments of source so they can then be
36 expanded out into code. Most of this could be done with CPP macros, but then
37 developers can't really step through them in the debugger, this way they are
38 expanded to the same code, but you can debug them.
39
40 Any file can be processed, but the syntax is designed around a C based compiler.
41 Processed lines start with "//%". There are three types of sections you can
42 create: Text (left alone), Macro Definitions, and Macro Expansions. There is
43 no order required between definitions and expansions, all definitions are read
44 before any expansions are processed (thus, if desired, definitions can be put
45 at the end of the file to keep them out of the way of the code).
46
47 Macro Definitions are started with "//%PDDM-DEFINE Name(args)" and all lines
48 afterwards that start with "//%" are included in the definition. Multiple
49 macros can be defined in one block by just using a another "//%PDDM-DEFINE"
50 line to start the next macro. Optionally, a macro can be ended with
51 "//%PDDM-DEFINE-END", this can be useful when you want to make it clear that
52 trailing blank lines are included in the macro. You can also end a definition
53 with an expansion.
54
55 Macro Expansions are started by single lines containing
56 "//%PDDM-EXPAND Name(args)" and then with "//%PDDM-EXPAND-END" or another
57 expansions. All lines in-between are replaced by the result of the expansion.
58 The first line of the expansion is always a blank like just for readability.
59
60 Expansion itself is pretty simple, one macro can invoke another macro, but
61 you cannot nest the invoke of a macro in another macro (i.e. - can't do
62 "foo(bar(a))", but you can define foo(a) and bar(b) where bar invokes foo()
63 within its expansion.
64
65 When macros are expanded, the arg references can also add "$O" suffix to the
66 name (i.e. - "NAME$O") to specify an option to be applied. The options are:
67
68 $S - Replace each character in the value with a space.
69 $l - Lowercase the first letter of the value.
70 $L - Lowercase the whole value.
71 $u - Uppercase the first letter of the value.
72 $U - Uppercase the whole value.
73
74 Within a macro you can use ## to cause things to get joined together after
75 expansion (i.e. - "a##b" within a macro will become "ab").
76
77 Example:
78
79 int foo(MyEnum x) {
80 switch (x) {
81 //%PDDM-EXPAND case(Enum_Left, 1)
82 //%PDDM-EXPAND case(Enum_Center, 2)
83 //%PDDM-EXPAND case(Enum_Right, 3)
84 //%PDDM-EXPAND-END
85 }
86
87 //%PDDM-DEFINE case(_A, _B)
88 //% case _A:
89 //% return _B;
90
91 A macro ends at the start of the next one, or an optional %PDDM-DEFINE-END
92 can be used to avoid adding extra blank lines/returns (or make it clear when
93 it is desired).
94
95 One macro can invoke another by simply using its name NAME(ARGS). You cannot
96 nest an invoke inside another (i.e. - NAME1(NAME2(ARGS)) isn't supported).
97
98 Within a macro you can use ## to cause things to get joined together after
99 processing (i.e. - "a##b" within a macro will become "ab").
100
101
102 """
103
104 import optparse
105 import os
106 import re
107 import sys
108
109
110 # Regex for macro definition.
111 _MACRO_RE = re.compile(r'(?P<name>\w+)\((?P<args>.*?)\)')
112 # Regex for macro's argument definition.
113 _MACRO_ARG_NAME_RE = re.compile(r'^\w+$')
114
115 # Line inserted after each EXPAND.
116 _GENERATED_CODE_LINE = (
117 '// This block of code is generated, do not edit it directly.'
118 )
119
120
121 def _MacroRefRe(macro_names):
122 # Takes in a list of macro names and makes a regex that will match invokes
123 # of those macros.
124 return re.compile(r'\b(?P<macro_ref>(?P<name>(%s))\((?P<args>.*?)\))' %
125 '|'.join(macro_names))
126
127 def _MacroArgRefRe(macro_arg_names):
128 # Takes in a list of macro arg names and makes a regex that will match
129 # uses of those args.
130 return re.compile(r'\b(?P<name>(%s))(\$(?P<option>.))?\b' %
131 '|'.join(macro_arg_names))
132
133
134 class PDDMError(Exception):
135 """Error thrown by pddm."""
136 pass
137
138
139 class MacroCollection(object):
140 """Hold a set of macros and can resolve/expand them."""
141
142 def __init__(self, a_file=None):
143 """Initializes the collection.
144
145 Args:
146 a_file: The file like stream to parse.
147
148 Raises:
149 PDDMError if there are any issues.
150 """
151 self._macros = dict()
152 if a_file:
153 self.ParseInput(a_file)
154
155 class MacroDefinition(object):
156 """Holds a macro definition."""
157
158 def __init__(self, name, arg_names):
159 self._name = name
160 self._args = tuple(arg_names)
161 self._body = ''
162 self._needNewLine = False
163
164 def AppendLine(self, line):
165 if self._needNewLine:
166 self._body += '\n'
167 self._body += line
168 self._needNewLine = not line.endswith('\n')
169
170 @property
171 def name(self):
172 return self._name
173
174 @property
175 def args(self):
176 return self._args
177
178 @property
179 def body(self):
180 return self._body
181
182 def ParseInput(self, a_file):
183 """Consumes input extracting definitions.
184
185 Args:
186 a_file: The file like stream to parse.
187
188 Raises:
189 PDDMError if there are any issues.
190 """
191 input_lines = a_file.read().splitlines()
192 self.ParseLines(input_lines)
193
194 def ParseLines(self, input_lines):
195 """Parses list of lines.
196
197 Args:
198 input_lines: A list of strings of input to parse (no newlines on the
199 strings).
200
201 Raises:
202 PDDMError if there are any issues.
203 """
204 current_macro = None
205 for line in input_lines:
206 if line.startswith('PDDM-'):
207 directive = line.split(' ', 1)[0]
208 if directive == 'PDDM-DEFINE':
209 name, args = self._ParseDefineLine(line)
210 if self._macros.get(name):
211 raise PDDMError('Attempt to redefine macro: "%s"' % line)
212 current_macro = self.MacroDefinition(name, args)
213 self._macros[name] = current_macro
214 continue
215 if directive == 'PDDM-DEFINE-END':
216 if not current_macro:
217 raise PDDMError('Got DEFINE-END directive without an active macro:'
218 ' "%s"' % line)
219 current_macro = None
220 continue
221 raise PDDMError('Hit a line with an unknown directive: "%s"' % line)
222
223 if current_macro:
224 current_macro.AppendLine(line)
225 continue
226
227 # Allow blank lines between macro definitions.
228 if line.strip() == '':
229 continue
230
231 raise PDDMError('Hit a line that wasn\'t a directive and no open macro'
232 ' definition: "%s"' % line)
233
234 def _ParseDefineLine(self, input_line):
235 assert input_line.startswith('PDDM-DEFINE')
236 line = input_line[12:].strip()
237 match = _MACRO_RE.match(line)
238 # Must match full line
239 if match is None or match.group(0) != line:
240 raise PDDMError('Failed to parse macro definition: "%s"' % input_line)
241 name = match.group('name')
242 args_str = match.group('args').strip()
243 args = []
244 if args_str:
245 for part in args_str.split(','):
246 arg = part.strip()
247 if arg == '':
248 raise PDDMError('Empty arg name in macro definition: "%s"'
249 % input_line)
250 if not _MACRO_ARG_NAME_RE.match(arg):
251 raise PDDMError('Invalid arg name "%s" in macro definition: "%s"'
252 % (arg, input_line))
253 if arg in args:
254 raise PDDMError('Arg name "%s" used more than once in macro'
255 ' definition: "%s"' % (arg, input_line))
256 args.append(arg)
257 return (name, tuple(args))
258
259 def Expand(self, macro_ref_str):
260 """Expands the macro reference.
261
262 Args:
263 macro_ref_str: String of a macro reference (i.e. foo(a, b)).
264
265 Returns:
266 The text from the expansion.
267
268 Raises:
269 PDDMError if there are any issues.
270 """
271 match = _MACRO_RE.match(macro_ref_str)
272 if match is None or match.group(0) != macro_ref_str:
273 raise PDDMError('Failed to parse macro reference: "%s"' % macro_ref_str)
274 if match.group('name') not in self._macros:
275 raise PDDMError('No macro named "%s".' % match.group('name'))
276 return self._Expand(match, [], macro_ref_str)
277
278 def _FormatStack(self, macro_ref_stack):
279 result = ''
280 for _, macro_ref in reversed(macro_ref_stack):
281 result += '\n...while expanding "%s".' % macro_ref
282 return result
283
284 def _Expand(self, macro_ref_match, macro_stack, macro_ref_str=None):
285 if macro_ref_str is None:
286 macro_ref_str = macro_ref_match.group('macro_ref')
287 name = macro_ref_match.group('name')
288 for prev_name, prev_macro_ref in macro_stack:
289 if name == prev_name:
290 raise PDDMError('Found macro recusion, invoking "%s":%s' %
291 (macro_ref_str, self._FormatStack(macro_stack)))
292 macro = self._macros[name]
293 args_str = macro_ref_match.group('args').strip()
294 args = []
295 if args_str or len(macro.args):
296 args = [x.strip() for x in args_str.split(',')]
297 if len(args) != len(macro.args):
298 raise PDDMError('Expected %d args, got: "%s".%s' %
299 (len(macro.args), macro_ref_str,
300 self._FormatStack(macro_stack)))
301 # Replace args usages.
302 result = self._ReplaceArgValues(macro, args, macro_ref_str, macro_stack)
303 # Expand any macro invokes.
304 new_macro_stack = macro_stack + [(name, macro_ref_str)]
305 while True:
306 eval_result = self._EvalMacrosRefs(result, new_macro_stack)
307 # Consume all ## directives to glue things together.
308 eval_result = eval_result.replace('##', '')
309 if eval_result == result:
310 break
311 result = eval_result
312 return result
313
314 def _ReplaceArgValues(self,
315 macro, arg_values, macro_ref_to_report, macro_stack):
316 if len(arg_values) == 0:
317 # Nothing to do
318 return macro.body
319 assert len(arg_values) == len(macro.args)
320 args = dict(zip(macro.args, arg_values))
321 def _lookupArg(match):
322 val = args[match.group('name')]
323 opt = match.group('option')
324 if opt:
325 if opt == 'S': # Spaces for the length
326 return ' ' * len(val)
327 elif opt == 'l': # Lowercase first character
328 if val:
329 return val[0].lower() + val[1:]
330 else:
331 return val
332 elif opt == 'L': # All Lowercase
333 return val.lower()
334 elif opt == 'u': # Uppercase first character
335 if val:
336 return val[0].upper() + val[1:]
337 else:
338 return val
339 elif opt == 'U': # All Uppercase
340 return val.upper()
341 else:
342 raise PDDMError('Unknown arg option "%s$%s" while expanding "%s".%s'
343 % (match.group('name'), match.group('option'),
344 macro_ref_to_report,
345 self._FormatStack(macro_stack)))
346 return val
347 # Let the regex do the work!
348 macro_arg_ref_re = _MacroArgRefRe(macro.args)
349 return macro_arg_ref_re.sub(_lookupArg, macro.body)
350
351 def _EvalMacrosRefs(self, text, macro_stack):
352 macro_ref_re = _MacroRefRe(self._macros.keys())
353 def _resolveMacro(match):
354 return self._Expand(match, macro_stack)
355 return macro_ref_re.sub(_resolveMacro, text)
356
357
358 class SourceFile(object):
359 """Represents a source file with PDDM directives in it."""
360
361 def __init__(self, a_file, import_resolver=None):
362 """Initializes the file reading in the file.
363
364 Args:
365 a_file: The file to read in.
366 import_resolver: a function that given a path will return a stream for
367 the contents.
368
369 Raises:
370 PDDMError if there are any issues.
371 """
372 self._sections = []
373 self._original_content = a_file.read()
374 self._import_resolver = import_resolver
375 self._processed_content = None
376
377 class SectionBase(object):
378
379 def __init__(self, first_line_num):
380 self._lines = []
381 self._first_line_num = first_line_num
382
383 def TryAppend(self, line, line_num):
384 """Try appending a line.
385
386 Args:
387 line: The line to append.
388 line_num: The number of the line.
389
390 Returns:
391 A tuple of (SUCCESS, CAN_ADD_MORE). If SUCCESS if False, the line
392 wasn't append. If SUCCESS is True, then CAN_ADD_MORE is True/False to
393 indicate if more lines can be added after this one.
394 """
395 assert False, "sublcass should have overridden"
396 return (False, False)
397
398 def HitEOF(self):
399 """Called when the EOF was reached for for a given section."""
400 pass
401
402 def BindMacroCollection(self, macro_collection):
403 """Binds the chunk to a macro collection.
404
405 Args:
406 macro_collection: The collection to bind too.
407 """
408 pass
409
410 def Append(self, line):
411 self._lines.append(line)
412
413 @property
414 def lines(self):
415 return self._lines
416
417 @property
418 def num_lines_captured(self):
419 return len(self._lines)
420
421 @property
422 def first_line_num(self):
423 return self._first_line_num
424
425 @property
426 def first_line(self):
427 if not self._lines:
428 return ''
429 return self._lines[0]
430
431 @property
432 def text(self):
433 return '\n'.join(self.lines) + '\n'
434
435 class TextSection(SectionBase):
436 """Text section that is echoed out as is."""
437
438 def TryAppend(self, line, line_num):
439 if line.startswith('//%PDDM'):
440 return (False, False)
441 self.Append(line)
442 return (True, True)
443
444 class ExpansionSection(SectionBase):
445 """Section that is the result of an macro expansion."""
446
447 def __init__(self, first_line_num):
448 SourceFile.SectionBase.__init__(self, first_line_num)
449 self._macro_collection = None
450
451 def TryAppend(self, line, line_num):
452 if line.startswith('//%PDDM'):
453 directive = line.split(' ', 1)[0]
454 if directive == '//%PDDM-EXPAND':
455 self.Append(line)
456 return (True, True)
457 if directive == '//%PDDM-EXPAND-END':
458 assert self.num_lines_captured > 0
459 return (True, False)
460 raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
461 (directive, line_num, self.first_line))
462 # Eat other lines.
463 return (True, True)
464
465 def HitEOF(self):
466 raise PDDMError('Hit the end of the file while in "%s".' %
467 self.first_line)
468
469 def BindMacroCollection(self, macro_collection):
470 self._macro_collection = macro_collection
471
472 @property
473 def lines(self):
474 captured_lines = SourceFile.SectionBase.lines.fget(self)
475 directive_len = len('//%PDDM-EXPAND')
476 result = []
477 for line in captured_lines:
478 result.append(line)
479 if self._macro_collection:
480 # Always add a blank line, seems to read better. (If need be, add an
481 # option to the EXPAND to indicate if this should be done.)
482 result.extend([_GENERATED_CODE_LINE, ''])
483 macro = line[directive_len:].strip()
484 try:
485 expand_result = self._macro_collection.Expand(macro)
486 # Since expansions are line oriented, strip trailing whitespace
487 # from the lines.
488 lines = [x.rstrip() for x in expand_result.split('\n')]
489 result.append('\n'.join(lines))
490 except PDDMError as e:
491 raise PDDMError('%s\n...while expanding "%s" from the section'
492 ' that started:\n Line %d: %s' %
493 (e.message, macro,
494 self.first_line_num, self.first_line))
495
496 # Add the ending marker.
497 if len(captured_lines) == 1:
498 result.append('//%%PDDM-EXPAND-END %s' %
499 captured_lines[0][directive_len:].strip())
500 else:
501 result.append('//%%PDDM-EXPAND-END (%s expansions)' % len(captured_lines ))
502
503 return result
504
505 class DefinitionSection(SectionBase):
506 """Section containing macro definitions"""
507
508 def TryAppend(self, line, line_num):
509 if not line.startswith('//%'):
510 return (False, False)
511 if line.startswith('//%PDDM'):
512 directive = line.split(' ', 1)[0]
513 if directive == "//%PDDM-EXPAND":
514 return False, False
515 if directive not in ('//%PDDM-DEFINE', '//%PDDM-DEFINE-END'):
516 raise PDDMError('Ran into directive ("%s", line %d) while in "%s".' %
517 (directive, line_num, self.first_line))
518 self.Append(line)
519 return (True, True)
520
521 def BindMacroCollection(self, macro_collection):
522 if macro_collection:
523 try:
524 # Parse the lines after stripping the prefix.
525 macro_collection.ParseLines([x[3:] for x in self.lines])
526 except PDDMError as e:
527 raise PDDMError('%s\n...while parsing section that started:\n'
528 ' Line %d: %s' %
529 (e.message, self.first_line_num, self.first_line))
530
531 class ImportDefinesSection(SectionBase):
532 """Section containing an import of PDDM-DEFINES from an external file."""
533
534 def __init__(self, first_line_num, import_resolver):
535 SourceFile.SectionBase.__init__(self, first_line_num)
536 self._import_resolver = import_resolver
537
538 def TryAppend(self, line, line_num):
539 if not line.startswith('//%PDDM-IMPORT-DEFINES '):
540 return (False, False)
541 assert self.num_lines_captured == 0
542 self.Append(line)
543 return (True, False)
544
545 def BindMacroCollection(self, macro_colletion):
546 if not macro_colletion:
547 return
548 if self._import_resolver is None:
549 raise PDDMError('Got an IMPORT-DEFINES without a resolver (line %d):'
550 ' "%s".' % (self.first_line_num, self.first_line))
551 import_name = self.first_line.split(' ', 1)[1].strip()
552 imported_file = self._import_resolver(import_name)
553 if imported_file is None:
554 raise PDDMError('Resolver failed to find "%s" (line %d):'
555 ' "%s".' %
556 (import_name, self.first_line_num, self.first_line))
557 try:
558 imported_src_file = SourceFile(imported_file, self._import_resolver)
559 imported_src_file._ParseFile()
560 for section in imported_src_file._sections:
561 section.BindMacroCollection(macro_colletion)
562 except PDDMError as e:
563 raise PDDMError('%s\n...while importing defines:\n'
564 ' Line %d: %s' %
565 (e.message, self.first_line_num, self.first_line))
566
567 def _ParseFile(self):
568 self._sections = []
569 lines = self._original_content.splitlines()
570 cur_section = None
571 for line_num, line in enumerate(lines, 1):
572 if not cur_section:
573 cur_section = self._MakeSection(line, line_num)
574 was_added, accept_more = cur_section.TryAppend(line, line_num)
575 if not was_added:
576 cur_section = self._MakeSection(line, line_num)
577 was_added, accept_more = cur_section.TryAppend(line, line_num)
578 assert was_added
579 if not accept_more:
580 cur_section = None
581
582 if cur_section:
583 cur_section.HitEOF()
584
585 def _MakeSection(self, line, line_num):
586 if not line.startswith('//%PDDM'):
587 section = self.TextSection(line_num)
588 else:
589 directive = line.split(' ', 1)[0]
590 if directive == '//%PDDM-EXPAND':
591 section = self.ExpansionSection(line_num)
592 elif directive == '//%PDDM-DEFINE':
593 section = self.DefinitionSection(line_num)
594 elif directive == '//%PDDM-IMPORT-DEFINES':
595 section = self.ImportDefinesSection(line_num, self._import_resolver)
596 else:
597 raise PDDMError('Unexpected line %d: "%s".' % (line_num, line))
598 self._sections.append(section)
599 return section
600
601 def ProcessContent(self, strip_expansion=False):
602 """Processes the file contents."""
603 self._ParseFile()
604 if strip_expansion:
605 # Without a collection the expansions become blank, removing them.
606 collection = None
607 else:
608 collection = MacroCollection()
609 for section in self._sections:
610 section.BindMacroCollection(collection)
611 result = ''
612 for section in self._sections:
613 result += section.text
614 self._processed_content = result
615
616 @property
617 def original_content(self):
618 return self._original_content
619
620 @property
621 def processed_content(self):
622 return self._processed_content
623
624
625 def main(args):
626 usage = '%prog [OPTIONS] PATH ...'
627 description = (
628 'Processes PDDM directives in the the given paths and write them back'
629 ' out.'
630 )
631 parser = optparse.OptionParser(usage=usage, description=description)
632 parser.add_option('--dry-run',
633 default=False, action='store_true',
634 help='Don\'t write back to the file(s), just report if the'
635 ' contents needs an update and exit with a value of 1.')
636 parser.add_option('--verbose',
637 default=False, action='store_true',
638 help='Reports is a file is already current.')
639 parser.add_option('--collapse',
640 default=False, action='store_true',
641 help='Removes all the generated code.')
642 opts, extra_args = parser.parse_args(args)
643
644 if not extra_args:
645 parser.error('Need atleast one file to process')
646
647 result = 0
648 for a_path in extra_args:
649 if not os.path.exists(a_path):
650 sys.stderr.write('ERROR: File not found: %s\n' % a_path)
651 return 100
652
653 def _ImportResolver(name):
654 # resolve based on the file being read.
655 a_dir = os.path.dirname(a_path)
656 import_path = os.path.join(a_dir, name)
657 if not os.path.exists(import_path):
658 return None
659 return open(import_path, 'r')
660
661 with open(a_path, 'r') as f:
662 src_file = SourceFile(f, _ImportResolver)
663
664 try:
665 src_file.ProcessContent(strip_expansion=opts.collapse)
666 except PDDMError as e:
667 sys.stderr.write('ERROR: %s\n...While processing "%s"\n' %
668 (e.message, a_path))
669 return 101
670
671 if src_file.processed_content != src_file.original_content:
672 if not opts.dry_run:
673 print 'Updating for "%s".' % a_path
674 with open(a_path, 'w') as f:
675 f.write(src_file.processed_content)
676 else:
677 # Special result to indicate things need updating.
678 print 'Update needed for "%s".' % a_path
679 result = 1
680 elif opts.verbose:
681 print 'No update for "%s".' % a_path
682
683 return result
684
685
686 if __name__ == '__main__':
687 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « third_party/protobuf/objectivec/DevTools/full_mac_build.sh ('k') | third_party/protobuf/objectivec/DevTools/pddm_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698