text: #!/usr/bin/env python
  #
  # Copyright (c) 2014, 2016 Apple Inc. All rights reserved.
  # Copyright (c) 2014 University of Washington. All rights reserved.
  #
  # Redistribution and use in source and binary forms, with or without
  # modification, are permitted provided that the following conditions
  # are met:
  # 1. Redistributions of source code must retain the above copyright
  #    notice, this list of conditions and the following disclaimer.
  # 2. Redistributions in binary form must reproduce the above copyright
  #    notice, this list of conditions and the following disclaimer in the
  #    documentation and/or other materials provided with the distribution.
  #
  # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
  # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  # THE POSSIBILITY OF SUCH DAMAGE.
  
  # This script generates JS, Objective C, and C++ bindings for the inspector protocol.
  # Generators for individual files are located in the codegen/ directory.
  
  import os
  import re
  import sys
  import string
  from string import Template
  import optparse
  import logging
  
  try:
      import json
  except ImportError:
      import simplejson as json
  
  logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR)
  log = logging.getLogger('global')
  
  try:
      from codegen import *
  
  # When copying generator files to JavaScriptCore's private headers on Mac,
  # the codegen/ module directory is flattened. So, import directly.
  except ImportError as e:
      # log.error(e) # Uncomment this to debug early import errors.
      import models
      from models import *
      from generator import *
      from cpp_generator import *
      from objc_generator import *
  
      from generate_cpp_alternate_backend_dispatcher_header import *
      from generate_cpp_backend_dispatcher_header import *
      from generate_cpp_backend_dispatcher_implementation import *
      from generate_cpp_frontend_dispatcher_header import *
      from generate_cpp_frontend_dispatcher_implementation import *
      from generate_cpp_protocol_types_header import *
      from generate_cpp_protocol_types_implementation import *
      from generate_js_backend_commands import *
      from generate_objc_backend_dispatcher_header import *
      from generate_objc_backend_dispatcher_implementation import *
      from generate_objc_configuration_header import *
      from generate_objc_configuration_implementation import *
      from generate_objc_frontend_dispatcher_implementation import *
      from generate_objc_header import *
      from generate_objc_internal_header import *
      from generate_objc_protocol_type_conversions_header import *
      from generate_objc_protocol_type_conversions_implementation import *
      from generate_objc_protocol_types_implementation import *
  
  
  # A writer that only updates file if it actually changed.
  class IncrementalFileWriter:
      def __init__(self, filepath, force_output):
          self._filepath = filepath
          self._output = ""
          self.force_output = force_output
  
      def write(self, text):
          self._output += text
  
      def close(self):
          text_changed = True
          self._output = self._output.rstrip() + "\n"
  
          try:
              if self.force_output:
                  raise
  
              read_file = open(self._filepath, "r")
              old_text = read_file.read()
              read_file.close()
              text_changed = old_text != self._output
          except:
              # Ignore, just overwrite by default
              pass
  
          if text_changed or self.force_output:
              dirname = os.path.dirname(self._filepath)
              if not os.path.isdir(dirname):
                  os.makedirs(dirname)
              out_file = open(self._filepath, "w")
              out_file.write(self._output)
              out_file.close()
  
  
  def generate_from_specification(primary_specification_filepath=None,
                                  supplemental_specification_filepaths=[],
                                  concatenate_output=False,
                                  output_dirpath=None,
                                  force_output=False,
                                  framework_name="",
                                  platform_name="",
                                  generate_frontend=True,
                                  generate_backend=True):
  
      def load_specification(protocol, filepath, isSupplemental=False):
          try:
              with open(filepath, "r") as input_file:
                  parsed_json = json.load(input_file)
                  protocol.parse_specification(parsed_json, isSupplemental)
          except ValueError as e:
              raise Exception("Error parsing valid JSON in file: " + filepath + "\nParse error: " + str(e))
  
      platform = Platform.fromString(platform_name)
      protocol = models.Protocol(framework_name)
      for specification in supplemental_specification_filepaths:
          load_specification(protocol, specification, isSupplemental=True)
      load_specification(protocol, primary_specification_filepath, isSupplemental=False)
  
      protocol.resolve_types()
  
      generator_arguments = [protocol, platform, primary_specification_filepath]
      generators = []
  
      if protocol.framework is Frameworks.Test:
          generators.append(JSBackendCommandsGenerator(*generator_arguments))
          generators.append(CppAlternateBackendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppBackendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppBackendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(CppFrontendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppFrontendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(CppProtocolTypesHeaderGenerator(*generator_arguments))
          generators.append(CppProtocolTypesImplementationGenerator(*generator_arguments))
          generators.append(ObjCBackendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(ObjCBackendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(ObjCConfigurationHeaderGenerator(*generator_arguments))
          generators.append(ObjCConfigurationImplementationGenerator(*generator_arguments))
          generators.append(ObjCFrontendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(ObjCHeaderGenerator(*generator_arguments))
          generators.append(ObjCInternalHeaderGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypeConversionsHeaderGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypeConversionsImplementationGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypesImplementationGenerator(*generator_arguments))
  
      elif protocol.framework is Frameworks.JavaScriptCore:
          generators.append(JSBackendCommandsGenerator(*generator_arguments))
          generators.append(CppAlternateBackendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppBackendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppBackendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(CppFrontendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppFrontendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(CppProtocolTypesHeaderGenerator(*generator_arguments))
          generators.append(CppProtocolTypesImplementationGenerator(*generator_arguments))
  
      elif protocol.framework is Frameworks.WebKit and generate_backend:
          generators.append(CppBackendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppBackendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(CppFrontendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(CppFrontendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(CppProtocolTypesHeaderGenerator(*generator_arguments))
          generators.append(CppProtocolTypesImplementationGenerator(*generator_arguments))
  
      elif protocol.framework is Frameworks.WebKit and generate_frontend:
          generators.append(ObjCHeaderGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypeConversionsHeaderGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypeConversionsImplementationGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypesImplementationGenerator(*generator_arguments))
  
      elif protocol.framework is Frameworks.WebInspector:
          generators.append(ObjCBackendDispatcherHeaderGenerator(*generator_arguments))
          generators.append(ObjCBackendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(ObjCConfigurationHeaderGenerator(*generator_arguments))
          generators.append(ObjCConfigurationImplementationGenerator(*generator_arguments))
          generators.append(ObjCFrontendDispatcherImplementationGenerator(*generator_arguments))
          generators.append(ObjCHeaderGenerator(*generator_arguments))
          generators.append(ObjCInternalHeaderGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypeConversionsHeaderGenerator(*generator_arguments))
          generators.append(ObjCProtocolTypesImplementationGenerator(*generator_arguments))
  
      elif protocol.framework is Frameworks.WebInspectorUI:
          generators.append(JSBackendCommandsGenerator(*generator_arguments))
  
      single_output_file_contents = []
  
      for generator in generators:
          # Only some generators care whether frontend or backend was specified, but it is
          # set on all of them to avoid adding more constructor arguments or thinking too hard.
          if generate_backend:
              generator.set_generator_setting('generate_backend', True)
          if generate_frontend:
              generator.set_generator_setting('generate_frontend', True)
  
          output = generator.generate_output()
          if concatenate_output:
              single_output_file_contents.append('### Begin File: %s' % generator.output_filename())
              single_output_file_contents.append(output)
              single_output_file_contents.append('### End File: %s' % generator.output_filename())
              single_output_file_contents.append('')
          else:
              output_file = IncrementalFileWriter(os.path.join(output_dirpath, generator.output_filename()), force_output)
              output_file.write(output)
              output_file.close()
  
      if concatenate_output:
          filename = os.path.join(os.path.basename(primary_specification_filepath) + '-result')
          output_file = IncrementalFileWriter(os.path.join(output_dirpath, filename), force_output)
          output_file.write('\n'.join(single_output_file_contents))
          output_file.close()
  
  
  if __name__ == '__main__':
      allowed_framework_names = ['JavaScriptCore', 'WebInspector', 'WebInspectorUI', 'WebKit', 'Test']
      allowed_platform_names = ['iOS', 'macOS', 'all', 'generic']
      cli_parser = optparse.OptionParser(usage="usage: %prog [options] PrimaryProtocol.json [SupplementalProtocol.json ...]")
      cli_parser.add_option("-o", "--outputDir", help="Directory where generated files should be written.")
      cli_parser.add_option("--framework", type="choice", choices=allowed_framework_names, help="The framework that the primary specification belongs to.")
      cli_parser.add_option("--force", action="store_true", help="Force output of generated scripts, even if nothing changed.")
      cli_parser.add_option("-v", "--debug", action="store_true", help="Log extra output for debugging the generator itself.")
      cli_parser.add_option("-t", "--test", action="store_true", help="Enable test mode. Use unique output filenames created by prepending the input filename.")
      cli_parser.add_option("--frontend", action="store_true", help="Generate code for the frontend-side of the protocol only.")
      cli_parser.add_option("--backend", action="store_true", help="Generate code for the backend-side of the protocol only.")
      cli_parser.add_option("--platform", default="generic", help="The platform of the backend being generated. For example, we compile WebKit2 for either macOS or iOS. This value is case-insensitive. Allowed values: %s" % ", ".join(allowed_platform_names))
      options = None
  
      arg_options, arg_values = cli_parser.parse_args()
      if (len(arg_values) < 1):
          raise ParseException("At least one plain argument expected")
  
      if not arg_options.outputDir:
          raise ParseException("Missing output directory")
  
      if arg_options.debug:
          log.setLevel(logging.DEBUG)
  
      generate_backend = arg_options.backend;
      generate_frontend = arg_options.frontend;
      # Default to generating both the frontend and backend if neither is specified.
      if not generate_backend and not generate_frontend:
          generate_backend = True
          generate_frontend = True
  
      options = {
          'primary_specification_filepath': arg_values[0],
          'supplemental_specification_filepaths': arg_values[1:],
          'output_dirpath': arg_options.outputDir,
          'concatenate_output': arg_options.test,
          'framework_name': arg_options.framework,
          'platform_name': arg_options.platform,
          'force_output': arg_options.force,
          'generate_backend': generate_backend,
          'generate_frontend': generate_frontend,
      }
  
      try:
          generate_from_specification(**options)
      except (ParseException, TypecheckException) as e:
          if arg_options.test:
              log.error(e.message)
          else:
              raise  # Force the build to fail.
  