Переглянути джерело

Format nanopb generator scripts (#10448)

Sam Edson 3 роки тому
батько
коміт
11229b59c6
2 змінених файлів з 352 додано та 358 видалено
  1. 136 135
      scripts/nanopb/nanopb_objc_generator.py
  2. 216 223
      scripts/nanopb/proto_generator.py

+ 136 - 135
scripts/nanopb/nanopb_objc_generator.py

@@ -37,185 +37,186 @@ nanopb_pb2 = nanopb.nanopb_pb2
 
 
 def main():
-  # Parse request
-  data = io.open(sys.stdin.fileno(), 'rb').read()
-  request = plugin_pb2.CodeGeneratorRequest.FromString(data)
+    # Parse request
+    data = io.open(sys.stdin.fileno(), 'rb').read()
+    request = plugin_pb2.CodeGeneratorRequest.FromString(data)
 
-  # Preprocess inputs, changing types and nanopb defaults
-  options = nanopb_parse_options(request)
-  use_anonymous_oneof(request)
-  use_bytes_for_strings(request)
+    # Preprocess inputs, changing types and nanopb defaults
+    options = nanopb_parse_options(request)
+    use_anonymous_oneof(request)
+    use_bytes_for_strings(request)
 
-  # Generate code
-  parsed_files = nanopb_parse_files(request, options)
-  results = nanopb_generate(request, options, parsed_files)
-  response = nanopb_write(results)
+    # Generate code
+    parsed_files = nanopb_parse_files(request, options)
+    results = nanopb_generate(request, options, parsed_files)
+    response = nanopb_write(results)
 
-  # Write to stdout
-  io.open(sys.stdout.fileno(), 'wb').write(response.SerializeToString())
+    # Write to stdout
+    io.open(sys.stdout.fileno(), 'wb').write(response.SerializeToString())
 
 
 def use_anonymous_oneof(request):
-  """Use anonymous unions for oneofs if they're the only one in a message.
+    """Use anonymous unions for oneofs if they're the only one in a message.
 
-  Equivalent to setting this option on messages where it applies:
+    Equivalent to setting this option on messages where it applies:
 
-    option (nanopb).anonymous_oneof = true;
+      option (nanopb).anonymous_oneof = true;
 
-  Args:
-    request: A CodeGeneratorRequest from protoc. The descriptors are modified
-      in place.
-  """
-  for _, message_type in iterate_messages(request):
-    if len(message_type.oneof_decl) == 1:
-      ext = message_type.options.Extensions[nanopb_pb2.nanopb_msgopt]
-      ext.anonymous_oneof = True
+    Args:
+      request: A CodeGeneratorRequest from protoc. The descriptors are modified
+        in place.
+    """
+    for _, message_type in iterate_messages(request):
+        if len(message_type.oneof_decl) == 1:
+            ext = message_type.options.Extensions[nanopb_pb2.nanopb_msgopt]
+            ext.anonymous_oneof = True
 
 
 def use_bytes_for_strings(request):
-  """Always use the bytes type instead of string.
+    """Always use the bytes type instead of string.
 
-  By default, nanopb renders proto strings as having the C type char* and does
-  not include a separate size field, getting the length of the string via
-  strlen(). Unfortunately this prevents using strings with embedded nulls,
-  which is something the wire format supports.
+    By default, nanopb renders proto strings as having the C type char* and does
+    not include a separate size field, getting the length of the string via
+    strlen(). Unfortunately this prevents using strings with embedded nulls,
+    which is something the wire format supports.
 
-  Fortunately, string and bytes proto fields are identical on the wire and
-  nanopb's bytes representation does have an explicit length, so this function
-  changes the types of all string fields to bytes. The generated code will now
-  contain pb_bytes_array_t.
+    Fortunately, string and bytes proto fields are identical on the wire and
+    nanopb's bytes representation does have an explicit length, so this function
+    changes the types of all string fields to bytes. The generated code will now
+    contain pb_bytes_array_t.
 
-  There's no nanopb or proto option to control this behavior. The equivalent
-  would be to hand edit all the .proto files :-(.
+    There's no nanopb or proto option to control this behavior. The equivalent
+    would be to hand edit all the .proto files :-(.
 
-  Args:
-    request: A CodeGeneratorRequest from protoc. The descriptors are modified
-      in place.
-  """
-  for names, message_type in iterate_messages(request):
-    for field in message_type.field:
-      if field.type == FieldDescriptorProto.TYPE_STRING:
-        field.type = FieldDescriptorProto.TYPE_BYTES
+    Args:
+      request: A CodeGeneratorRequest from protoc. The descriptors are modified
+        in place.
+    """
+    for names, message_type in iterate_messages(request):
+        for field in message_type.field:
+            if field.type == FieldDescriptorProto.TYPE_STRING:
+                field.type = FieldDescriptorProto.TYPE_BYTES
 
 
 def iterate_messages(request):
-  """Iterates over all messages in all files in the request.
+    """Iterates over all messages in all files in the request.
 
-  Args:
-    request: A CodeGeneratorRequest passed by protoc.
+    Args:
+      request: A CodeGeneratorRequest passed by protoc.
 
-  Yields:
-    names: a nanopb.Names object giving a qualified name for the message
-    message_type: a DescriptorProto for the message.
-  """
-  for fdesc in request.proto_file:
-    for names, message_type in nanopb.iterate_messages(fdesc):
-      yield names, message_type
+    Yields:
+      names: a nanopb.Names object giving a qualified name for the message
+      message_type: a DescriptorProto for the message.
+    """
+    for fdesc in request.proto_file:
+        for names, message_type in nanopb.iterate_messages(fdesc):
+            yield names, message_type
 
 
 def nanopb_parse_options(request):
-  """Parses nanopb_generator command-line options from the given request.
+    """Parses nanopb_generator command-line options from the given request.
 
-  Args:
-    request: A CodeGeneratorRequest passed by protoc.
+    Args:
+      request: A CodeGeneratorRequest passed by protoc.
 
-  Returns:
-    Nanopb's options object, obtained via optparser.
-  """
-  # Parse options the same as nanopb_generator.main_plugin() does.
-  args = shlex.split(request.parameter)
-  options, _ = nanopb.optparser.parse_args(args)
+    Returns:
+      Nanopb's options object, obtained via optparser.
+    """
+    # Parse options the same as nanopb_generator.main_plugin() does.
+    args = shlex.split(request.parameter)
+    options, _ = nanopb.optparser.parse_args(args)
 
-  # Force certain options
-  options.extension = '.nanopb'
-  options.verbose = True
+    # Force certain options
+    options.extension = '.nanopb'
+    options.verbose = True
 
-  # Replicate options setup from nanopb_generator.main_plugin.
-  nanopb.Globals.verbose_options = options.verbose
+    # Replicate options setup from nanopb_generator.main_plugin.
+    nanopb.Globals.verbose_options = options.verbose
 
-  # Google's protoc does not currently indicate the full path of proto files.
-  # Instead always add the main file path to the search dirs, that works for
-  # the common case.
-  options.options_path.append(os.path.dirname(request.file_to_generate[0]))
-  return options
+    # Google's protoc does not currently indicate the full path of proto files.
+    # Instead always add the main file path to the search dirs, that works for
+    # the common case.
+    options.options_path.append(os.path.dirname(request.file_to_generate[0]))
+    return options
 
 
 def nanopb_parse_files(request, options):
-  """Parses the files in the given request into nanopb ProtoFile objects.
-
-  Args:
-    request: A CodeGeneratorRequest, as passed by protoc.
-    options: The command-line options from nanopb_parse_options.
-
-  Returns:
-    A dictionary of filename to nanopb.ProtoFile objects, each one representing
-    the parsed form of a FileDescriptor in the request.
-  """
-  # Process any include files first, in order to have them
-  # available as dependencies
-  parsed_files = {}
-  for fdesc in request.proto_file:
-    parsed_files[fdesc.name] = nanopb.parse_file(fdesc.name, fdesc, options)
+    """Parses the files in the given request into nanopb ProtoFile objects.
+
+    Args:
+      request: A CodeGeneratorRequest, as passed by protoc.
+      options: The command-line options from nanopb_parse_options.
+
+    Returns:
+      A dictionary of filename to nanopb.ProtoFile objects, each one
+      representing the parsed form of a FileDescriptor in the request.
+    """
+    # Process any include files first, in order to have them
+    # available as dependencies
+    parsed_files = {}
+    for fdesc in request.proto_file:
+        parsed_files[fdesc.name] = nanopb.parse_file(fdesc.name, fdesc, options)
 
-  return parsed_files
+    return parsed_files
 
 
 def nanopb_generate(request, options, parsed_files):
-  """Generates C sources from the given parsed files.
-
-  Args:
-    request: A CodeGeneratorRequest, as passed by protoc.
-    options: The command-line options from nanopb_parse_options.
-    parsed_files: A dictionary of filename to nanopb.ProtoFile, as returned by
-      nanopb_parse_files().
-
-  Returns:
-    A list of nanopb output dictionaries, each one representing the code
-    generation result for each file to generate. The output dictionaries have
-    the following form:
-
-        {
-          'headername': Name of header file, ending in .h,
-          'headerdata': Contents of the header file,
-          'sourcename': Name of the source code file, ending in .c,
-          'sourcedata': Contents of the source code file
-        }
-  """
-  output = []
-
-  for filename in request.file_to_generate:
-    for fdesc in request.proto_file:
-      if fdesc.name == filename:
-        results = nanopb.process_file(filename, fdesc, options, parsed_files)
-        output.append(results)
-
-  return output
+    """Generates C sources from the given parsed files.
+
+    Args:
+      request: A CodeGeneratorRequest, as passed by protoc.
+      options: The command-line options from nanopb_parse_options.
+      parsed_files: A dictionary of filename to nanopb.ProtoFile, as returned by
+        nanopb_parse_files().
+
+    Returns:
+      A list of nanopb output dictionaries, each one representing the code
+      generation result for each file to generate. The output dictionaries have
+      the following form:
+
+          {
+            'headername': Name of header file, ending in .h,
+            'headerdata': Contents of the header file,
+            'sourcename': Name of the source code file, ending in .c,
+            'sourcedata': Contents of the source code file
+          }
+    """
+    output = []
+
+    for filename in request.file_to_generate:
+        for fdesc in request.proto_file:
+            if fdesc.name == filename:
+                results = nanopb.process_file(
+                    filename, fdesc, options, parsed_files)
+                output.append(results)
+
+    return output
 
 
 def nanopb_write(results):
-  """Translates nanopb output dictionaries to a CodeGeneratorResponse.
+    """Translates nanopb output dictionaries to a CodeGeneratorResponse.
 
-  Args:
-    results: A list of generated source dictionaries, as returned by
-      nanopb_generate().
+    Args:
+      results: A list of generated source dictionaries, as returned by
+        nanopb_generate().
 
-  Returns:
-    A CodeGeneratorResponse describing the result of the code generation
-    process to protoc.
-  """
-  response = plugin_pb2.CodeGeneratorResponse()
+    Returns:
+      A CodeGeneratorResponse describing the result of the code generation
+      process to protoc.
+    """
+    response = plugin_pb2.CodeGeneratorResponse()
 
-  for result in results:
-    f = response.file.add()
-    f.name = result['headername']
-    f.content = result['headerdata']
+    for result in results:
+        f = response.file.add()
+        f.name = result['headername']
+        f.content = result['headerdata']
 
-    f = response.file.add()
-    f.name = result['sourcename']
-    f.content = result['sourcedata']
+        f = response.file.add()
+        f.name = result['sourcename']
+        f.content = result['sourcedata']
 
-  return response
+    return response
 
 
 if __name__ == '__main__':
-  main()
+    main()

+ 216 - 223
scripts/nanopb/proto_generator.py

@@ -37,7 +37,7 @@ import re
 import subprocess
 
 
-OBJC_GENERATOR='nanopb_objc_generator.py'
+OBJC_GENERATOR = 'nanopb_objc_generator.py'
 
 COPYRIGHT_NOTICE = '''
 /*
@@ -59,296 +59,289 @@ COPYRIGHT_NOTICE = '''
 
 
 def main():
-  parser = argparse.ArgumentParser(
-      description='Generates proto messages.')
-  parser.add_argument(
-      '--nanopb', action='store_true',
-      help='Generates nanopb messages.')
-  parser.add_argument(
-      '--objc', action='store_true',
-      help='Generates Objective-C messages.')
-  parser.add_argument(
-      '--protos_dir',
-      help='Source directory containing .proto files.')
-  parser.add_argument(
-      '--output_dir', '-d',
-      help='Directory to write files; subdirectories will be created.')
-  parser.add_argument(
-      '--protoc', default='protoc',
-      help='Location of the protoc executable')
-  parser.add_argument(
-      '--pythonpath',
-      help='Location of the protoc python library.')
-  parser.add_argument(
-      '--include', '-I', action='append', default=[],
-      help='Adds INCLUDE to the proto path.')
-  parser.add_argument(
-      '--include_prefix', '-p', action='append', default=[],
-      help='Adds include_prefix to the <product>.nanopb.h include in .nanopb.c')
-
-
-  args = parser.parse_args()
-  if args.nanopb is None and args.objc is None:
-    parser.print_help()
-    sys.exit(1)
-
-  if args.protos_dir is None:
-    root_dir = os.path.abspath(os.path.dirname(__file__))
-    args.protos_dir = os.path.join(root_dir, 'protos')
-
-  if args.output_dir is None:
-    root_dir = os.path.abspath(os.path.dirname(__file__))
-    args.output_dir = os.path.join(root_dir, 'protogen-please-supply-an-outputdir')
-
-  all_proto_files = collect_files(args.protos_dir, '.proto')
-  if args.nanopb:
-    NanopbGenerator(args, all_proto_files).run()
-
-  proto_files = remove_well_known_protos(all_proto_files)
-
-  if args.objc:
-    ObjcProtobufGenerator(args, proto_files).run()
+    parser = argparse.ArgumentParser(
+        description='Generates proto messages.')
+    parser.add_argument(
+        '--nanopb', action='store_true',
+        help='Generates nanopb messages.')
+    parser.add_argument(
+        '--objc', action='store_true',
+        help='Generates Objective-C messages.')
+    parser.add_argument(
+        '--protos_dir',
+        help='Source directory containing .proto files.')
+    parser.add_argument(
+        '--output_dir', '-d',
+        help='Directory to write files; subdirectories will be created.')
+    parser.add_argument(
+        '--protoc', default='protoc',
+        help='Location of the protoc executable')
+    parser.add_argument(
+        '--pythonpath',
+        help='Location of the protoc python library.')
+    parser.add_argument(
+        '--include', '-I', action='append', default=[],
+        help='Adds INCLUDE to the proto path.')
+    parser.add_argument(
+        '--include_prefix', '-p', action='append', default=[],
+        help='Adds include_prefix to the <product>.nanopb.h include in'
+             ' .nanopb.c')
+
+    args = parser.parse_args()
+    if args.nanopb is None and args.objc is None:
+        parser.print_help()
+        sys.exit(1)
+
+    if args.protos_dir is None:
+        root_dir = os.path.abspath(os.path.dirname(__file__))
+        args.protos_dir = os.path.join(root_dir, 'protos')
+
+    if args.output_dir is None:
+        root_dir = os.path.abspath(os.path.dirname(__file__))
+        args.output_dir = os.path.join(
+            root_dir, 'protogen-please-supply-an-outputdir')
+
+    all_proto_files = collect_files(args.protos_dir, '.proto')
+    if args.nanopb:
+        NanopbGenerator(args, all_proto_files).run()
+
+    if args.objc:
+        print('Generating objc code is unsupported because it depends on the'
+              'main protobuf Podspec that adds a lot of size to SDKs.')
 
 
 class NanopbGenerator(object):
-  """Builds and runs the nanopb plugin to protoc."""
+    """Builds and runs the nanopb plugin to protoc."""
 
-  def __init__(self, args, proto_files):
-    self.args = args
-    self.proto_files = proto_files
+    def __init__(self, args, proto_files):
+        self.args = args
+        self.proto_files = proto_files
 
-  def run(self):
-    """Performs the action of the generator."""
+    def run(self):
+        """Performs the action of the generator."""
 
-    nanopb_out = os.path.join(self.args.output_dir, 'nanopb')
-    mkdir(nanopb_out)
+        nanopb_out = os.path.join(self.args.output_dir, 'nanopb')
+        mkdir(nanopb_out)
 
-    self.__run_generator(nanopb_out)
+        self.__run_generator(nanopb_out)
 
-    sources = collect_files(nanopb_out, '.nanopb.h', '.nanopb.c')
-    post_process_files(
-        sources,
-        add_copyright,
-        nanopb_remove_extern_c,
-        nanopb_rename_delete,
-        nanopb_use_module_import,
-        make_use_absolute_import(nanopb_out, self.args)
-    )
+        sources = collect_files(nanopb_out, '.nanopb.h', '.nanopb.c')
+        post_process_files(
+            sources,
+            add_copyright,
+            nanopb_remove_extern_c,
+            nanopb_rename_delete,
+            nanopb_use_module_import,
+            make_use_absolute_import(nanopb_out, self.args)
+        )
 
-  def __run_generator(self, out_dir):
-    """Invokes protoc using the nanopb plugin."""
-    cmd = protoc_command(self.args)
+    def __run_generator(self, out_dir):
+        """Invokes protoc using the nanopb plugin."""
+        cmd = protoc_command(self.args)
 
-    gen = os.path.join(os.path.dirname(__file__), OBJC_GENERATOR)
-    cmd.append('--plugin=protoc-gen-nanopb=%s' % gen)
+        gen = os.path.join(os.path.dirname(__file__), OBJC_GENERATOR)
+        cmd.append('--plugin=protoc-gen-nanopb=%s' % gen)
 
-    nanopb_flags = [
-        '--extension=.nanopb',
-        '--source-extension=.c',
-        '--no-timestamp'
-    ]
-    nanopb_flags.extend(['-I%s' % path for path in self.args.include])
-    cmd.append('--nanopb_out=%s:%s' % (' '.join(nanopb_flags), out_dir))
+        nanopb_flags = [
+            '--extension=.nanopb',
+            '--source-extension=.c',
+            '--no-timestamp'
+        ]
+        nanopb_flags.extend(['-I%s' % path for path in self.args.include])
+        cmd.append('--nanopb_out=%s:%s' % (' '.join(nanopb_flags), out_dir))
 
-    cmd.extend(self.proto_files)
-    run_protoc(self.args, cmd)
+        cmd.extend(self.proto_files)
+        run_protoc(self.args, cmd)
 
 
 def protoc_command(args):
-  """Composes the initial protoc command-line including its include path."""
-  cmd = [args.protoc]
-  if args.include is not None:
-    cmd.extend(['-I=%s' % path for path in args.include])
-  return cmd
+    """Composes the initial protoc command-line including its include path."""
+    cmd = [args.protoc]
+    if args.include is not None:
+        cmd.extend(['-I=%s' % path for path in args.include])
+    return cmd
 
 
 def run_protoc(args, cmd):
-  """Actually runs the given protoc command.
+    """Actually runs the given protoc command.
 
-  Args:
-    args: The command-line args (including pythonpath)
-    cmd: The command to run expressed as a list of strings
-  """
-  kwargs = {}
-  if args.pythonpath:
-    env = os.environ.copy()
-    old_path = env.get('PYTHONPATH')
-    env['PYTHONPATH'] = os.path.expanduser(args.pythonpath)
-    if old_path is not None:
-      env['PYTHONPATH'] += os.pathsep + old_path
-    kwargs['env'] = env
-
-  try:
-    outputString = subprocess.check_output(cmd, stderr=subprocess.STDOUT, **kwargs)
-    print(outputString.decode("utf-8"))
-  except subprocess.CalledProcessError as error:
-    print('command failed: ', ' '.join(cmd), '\nerror: ', error.output)
-
-
-def remove_well_known_protos(filenames):
-  """Remove "well-known" protos for objc.
-
-  On those platforms we get these for free as a part of the protobuf runtime.
-  We only need them for nanopb.
-
-  Args:
-    filenames: A list of filenames, each naming a .proto file.
-
-  Returns:
-    The filenames with members of google/protobuf removed.
-  """
-
-  return [f for f in filenames if 'protos/google/protobuf/' not in f]
+    Args:
+      args: The command-line args (including pythonpath)
+      cmd: The command to run expressed as a list of strings
+    """
+    kwargs = {}
+    if args.pythonpath:
+        env = os.environ.copy()
+        old_path = env.get('PYTHONPATH')
+        env['PYTHONPATH'] = os.path.expanduser(args.pythonpath)
+        if old_path is not None:
+            env['PYTHONPATH'] += os.pathsep + old_path
+        kwargs['env'] = env
+
+    try:
+        outputString = subprocess.check_output(
+            cmd, stderr=subprocess.STDOUT, **kwargs)
+        print(outputString.decode("utf-8"))
+    except subprocess.CalledProcessError as error:
+        print('command failed: ', ' '.join(cmd), '\nerror: ', error.output)
 
 
 def post_process_files(filenames, *processors):
-  for filename in filenames:
-    lines = []
-    with open(filename, 'r') as fd:
-      lines = fd.readlines()
+    for filename in filenames:
+        lines = []
+        with open(filename, 'r') as fd:
+            lines = fd.readlines()
 
-    for processor in processors:
-      sig = signature(processor)
-      if len(sig.parameters) == 1:
-        lines = processor(lines)
-      else:
-        lines = processor(lines, filename)
+        for processor in processors:
+            sig = signature(processor)
+            if len(sig.parameters) == 1:
+                lines = processor(lines)
+            else:
+                lines = processor(lines, filename)
 
-    write_file(filename, lines)
+        write_file(filename, lines)
 
 
 def write_file(filename, lines):
-  mkdir(os.path.dirname(filename))
-  with open(filename, 'w') as fd:
-    fd.write(''.join(lines))
+    mkdir(os.path.dirname(filename))
+    with open(filename, 'w') as fd:
+        fd.write(''.join(lines))
 
 
 def add_copyright(lines):
-  """Adds a copyright notice to the lines."""
-  if COPYRIGHT_NOTICE in lines:
-    return lines
-  result = [COPYRIGHT_NOTICE, '\n']
-  result.extend(lines)
-  return result
+    """Adds a copyright notice to the lines."""
+    if COPYRIGHT_NOTICE in lines:
+        return lines
+    result = [COPYRIGHT_NOTICE, '\n']
+    result.extend(lines)
+    return result
 
 
 def nanopb_remove_extern_c(lines):
-  """Removes extern "C" directives from nanopb code.
+    """Removes extern "C" directives from nanopb code.
 
-  Args:
-    lines: A nanobp-generated source file, split into lines.
-  Returns:
-    A list of strings, similar to the input but modified to remove extern "C".
-  """
-  result = []
-  state = 'initial'
-  for line in lines:
-    if state == 'initial':
-      if '#ifdef __cplusplus' in line:
-        state = 'in-ifdef'
-        continue
+    Args:
+      lines: A nanobp-generated source file, split into lines.
+    Returns:
+      A list of strings, similar to the input but modified to remove extern "C".
+    """
+    result = []
+    state = 'initial'
+    for line in lines:
+        if state == 'initial':
+            if '#ifdef __cplusplus' in line:
+                state = 'in-ifdef'
+                continue
 
-      result.append(line)
+            result.append(line)
 
-    elif state == 'in-ifdef':
-      if '#endif' in line:
-        state = 'initial'
+        elif state == 'in-ifdef':
+            if '#endif' in line:
+                state = 'initial'
 
-  return result
+    return result
 
 
 def nanopb_rename_delete(lines):
-  """Renames a delete symbol to delete_.
+    """Renames a delete symbol to delete_.
 
-  If a proto uses a field named 'delete', nanopb happily uses that in the
-  message definition. Works fine for C; not so much for C++.
+    If a proto uses a field named 'delete', nanopb happily uses that in the
+    message definition. Works fine for C; not so much for C++.
 
-  Args:
-    lines: The lines to fix.
+    Args:
+      lines: The lines to fix.
 
-  Returns:
-    The lines, fixed.
-  """
-  delete_keyword = re.compile(r'\bdelete\b')
-  return [delete_keyword.sub('delete_', line) for line in lines]
+    Returns:
+      The lines, fixed.
+    """
+    delete_keyword = re.compile(r'\bdelete\b')
+    return [delete_keyword.sub('delete_', line) for line in lines]
 
 
+# Don't let Copybara alter these lines.
 def nanopb_use_module_import(lines):
-  """Changes #include <pb.h> to include <nanopb/pb.h>""" # Don't let Copybara alter these lines.
-  return [line.replace('#include <pb.h>', '{}include <nanopb/pb.h>'.format("#")) for line in lines]
+    """Changes #include <pb.h> to include <nanopb/pb.h>"""
+    return [line.replace('#include <pb.h>',
+            '{}include <nanopb/pb.h>'.format("#"))
+            for line in lines]
+
 
 def make_use_absolute_import(nanopb_out, args):
-  import_file = collect_files(nanopb_out, '.nanopb.h')[0]
+    import_file = collect_files(nanopb_out, '.nanopb.h')[0]
 
-  def nanopb_use_absolute_import(lines, filename):
-    """Makes repo-relative imports
+    def nanopb_use_absolute_import(lines, filename):
+        """Makes repo-relative imports
 
-       #include "crashlytics.nanopb.h" =>
-       #include "Crashlytics/Protogen/nanopb/crashlytics.nanopb.h"
+           #include "crashlytics.nanopb.h" =>
+           #include "Crashlytics/Protogen/nanopb/crashlytics.nanopb.h"
 
-       This only applies to .nanopb.c files because it causes errors if
-       .nanopb.h files import other .nanopb.h files with full relative paths.
-    """
-    if ".h" in filename:
-      return lines
-    include_prefix = args.include_prefix[0]
-    header = os.path.basename(import_file)
-    return [line.replace('#include "{0}"'.format(header), '#include "{0}{1}"'.format(include_prefix, header)) for line in lines]
+           This only applies to .nanopb.c files because it causes errors if
+           .nanopb.h files import other .nanopb.h files with full relative
+           paths.
+        """
+        if ".h" in filename:
+            return lines
+        include_prefix = args.include_prefix[0]
+        header = os.path.basename(import_file)
+        return [line.replace('#include "{0}"'.format(header),
+                '#include "{0}{1}"'.format(include_prefix, header))
+                for line in lines]
+
+    return nanopb_use_absolute_import
 
-  return nanopb_use_absolute_import
 
 def strip_trailing_whitespace(lines):
-  """Removes trailing whitespace from the given lines."""
-  return [line.rstrip() + '\n' for line in lines]
+    """Removes trailing whitespace from the given lines."""
+    return [line.rstrip() + '\n' for line in lines]
 
 
 def objc_flatten_imports(lines):
-  """Flattens the import statements for compatibility with CocoaPods."""
+    """Flattens the import statements for compatibility with CocoaPods."""
 
-  long_import = re.compile(r'#import ".*/')
-  return [long_import.sub('#import "', line) for line in lines]
+    long_import = re.compile(r'#import ".*/')
+    return [long_import.sub('#import "', line) for line in lines]
 
 
 def objc_strip_extension_registry(lines):
-  """Removes extensionRegistry methods from the classes."""
-  skip = False
-  result = []
-  for line in lines:
-    if '+ (GPBExtensionRegistry*)extensionRegistry {' in line:
-      skip = True
-    if not skip:
-      result.append(line)
-    elif line == '}\n':
-      skip = False
+    """Removes extensionRegistry methods from the classes."""
+    skip = False
+    result = []
+    for line in lines:
+        if '+ (GPBExtensionRegistry*)extensionRegistry {' in line:
+            skip = True
+        if not skip:
+            result.append(line)
+        elif line == '}\n':
+            skip = False
 
-  return result
+    return result
 
 
 def collect_files(root_dir, *extensions):
-  """Finds files with the given extensions in the root_dir.
-
-  Args:
-    root_dir: The directory from which to start traversing.
-    *extensions: Filename extensions (including the leading dot) to find.
-
-  Returns:
-    A list of filenames, all starting with root_dir, that have one of the given
-    extensions.
-  """
-  result = []
-  for root, _, files in os.walk(root_dir):
-    for basename in files:
-      for ext in extensions:
-        if basename.endswith(ext):
-          filename = os.path.join(root, basename)
-          result.append(filename)
-  return result
+    """Finds files with the given extensions in the root_dir.
+
+    Args:
+      root_dir: The directory from which to start traversing.
+      *extensions: Filename extensions (including the leading dot) to find.
+
+    Returns:
+      A list of filenames, all starting with root_dir, that have one of the
+      given extensions.
+    """
+    result = []
+    for root, _, files in os.walk(root_dir):
+        for basename in files:
+            for ext in extensions:
+                if basename.endswith(ext):
+                    filename = os.path.join(root, basename)
+                    result.append(filename)
+    return result
 
 
 def mkdir(dirname):
-  if not os.path.isdir(dirname):
-    os.makedirs(dirname)
+    if not os.path.isdir(dirname):
+        os.makedirs(dirname)
 
 
 if __name__ == '__main__':
-  main()
+    main()