3 # Copyright (c) 2014 Apple Inc. All rights reserved.
4 # Copyright (c) 2014 University of Washington. All rights reserved.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
15 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 # THE POSSIBILITY OF SUCH DAMAGE.
27 # This script generates JS, Objective C, and C++ bindings for the inspector protocol.
28 # Generators for individual files are located in the codegen/ directory.
34 from string
import Template
41 import simplejson
as json
43 logging
.basicConfig(format
='%(levelname)s: %(message)s', level
=logging
.ERROR
)
44 log
= logging
.getLogger('global')
49 # When copying generator files to JavaScriptCore's private headers on Mac,
50 # the codegen/ module directory is flattened. So, import directly.
51 except ImportError, e
:
52 # log.error(e) # Uncomment this to debug early import errors.
55 from generator
import *
56 from cpp_generator
import *
57 from objc_generator
import *
59 from generate_cpp_alternate_backend_dispatcher_header
import *
60 from generate_cpp_backend_dispatcher_header
import *
61 from generate_cpp_backend_dispatcher_implementation
import *
62 from generate_cpp_frontend_dispatcher_header
import *
63 from generate_cpp_frontend_dispatcher_implementation
import *
64 from generate_cpp_protocol_types_header
import *
65 from generate_cpp_protocol_types_implementation
import *
66 from generate_js_backend_commands
import *
67 from generate_objc_backend_dispatcher_header
import *
68 from generate_objc_backend_dispatcher_implementation
import *
69 from generate_objc_configuration_header
import *
70 from generate_objc_configuration_implementation
import *
71 from generate_objc_conversion_helpers
import *
72 from generate_objc_frontend_dispatcher_implementation
import *
73 from generate_objc_header
import *
74 from generate_objc_internal_header
import *
75 from generate_objc_protocol_types_implementation
import *
78 # A writer that only updates file if it actually changed.
79 class IncrementalFileWriter
:
80 def __init__(self
, filepath
, force_output
):
81 self
._filepath
= filepath
83 self
.force_output
= force_output
85 def write(self
, text
):
90 self
._output
= self
._output
.rstrip() + "\n"
96 read_file
= open(self
._filepath
, "r")
97 old_text
= read_file
.read()
99 text_changed
= old_text
!= self
._output
101 # Ignore, just overwrite by default
104 if text_changed
or self
.force_output
:
105 out_file
= open(self
._filepath
, "w")
106 out_file
.write(self
._output
)
110 def generate_from_specification(primary_specification_filepath
=None,
111 supplemental_specification_filepaths
=[],
112 concatenate_output
=False,
117 def load_specification(protocol
, filepath
, isSupplemental
=False):
119 with open(filepath
, "r") as input_file
:
120 parsed_json
= json
.load(input_file
)
121 protocol
.parse_specification(parsed_json
, isSupplemental
)
122 except ValueError as e
:
123 raise Exception("Error parsing valid JSON in file: " + filepath
)
125 protocol
= models
.Protocol(framework_name
)
126 for specification
in supplemental_specification_filepaths
:
127 load_specification(protocol
, specification
, isSupplemental
=True)
128 load_specification(protocol
, primary_specification_filepath
, isSupplemental
=False)
130 protocol
.resolve_types()
133 is_test
= protocol
.framework
is Frameworks
.Test
134 if is_test
or protocol
.framework
is not Frameworks
.WebInspector
:
135 generators
.append(CppAlternateBackendDispatcherHeaderGenerator(protocol
, primary_specification_filepath
))
136 generators
.append(JSBackendCommandsGenerator(protocol
, primary_specification_filepath
))
137 generators
.append(CppBackendDispatcherHeaderGenerator(protocol
, primary_specification_filepath
))
138 generators
.append(CppBackendDispatcherImplementationGenerator(protocol
, primary_specification_filepath
))
139 generators
.append(CppFrontendDispatcherHeaderGenerator(protocol
, primary_specification_filepath
))
140 generators
.append(CppFrontendDispatcherImplementationGenerator(protocol
, primary_specification_filepath
))
141 generators
.append(CppProtocolTypesHeaderGenerator(protocol
, primary_specification_filepath
))
142 generators
.append(CppProtocolTypesImplementationGenerator(protocol
, primary_specification_filepath
))
143 if is_test
or protocol
.framework
is Frameworks
.WebInspector
:
144 generators
.append(ObjCBackendDispatcherHeaderGenerator(protocol
, primary_specification_filepath
))
145 generators
.append(ObjCBackendDispatcherImplementationGenerator(protocol
, primary_specification_filepath
))
146 generators
.append(ObjCConfigurationHeaderGenerator(protocol
, primary_specification_filepath
))
147 generators
.append(ObjCConfigurationImplementationGenerator(protocol
, primary_specification_filepath
))
148 generators
.append(ObjCConversionHelpersGenerator(protocol
, primary_specification_filepath
))
149 generators
.append(ObjCFrontendDispatcherImplementationGenerator(protocol
, primary_specification_filepath
))
150 generators
.append(ObjCHeaderGenerator(protocol
, primary_specification_filepath
))
151 generators
.append(ObjCProtocolTypesImplementationGenerator(protocol
, primary_specification_filepath
))
152 generators
.append(ObjCInternalHeaderGenerator(protocol
, primary_specification_filepath
))
154 single_output_file_contents
= []
156 for generator
in generators
:
157 output
= generator
.generate_output()
158 if concatenate_output
:
159 single_output_file_contents
.append('### Begin File: %s' % generator
.output_filename())
160 single_output_file_contents
.append(output
)
161 single_output_file_contents
.append('### End File: %s' % generator
.output_filename())
162 single_output_file_contents
.append('')
164 output_file
= IncrementalFileWriter(os
.path
.join(output_dirpath
, generator
.output_filename()), force_output
)
165 output_file
.write(output
)
168 if concatenate_output
:
169 filename
= os
.path
.join(os
.path
.basename(primary_specification_filepath
) + '-result')
170 output_file
= IncrementalFileWriter(os
.path
.join(output_dirpath
, filename
), force_output
)
171 output_file
.write('\n'.join(single_output_file_contents
))
175 if __name__
== '__main__':
176 allowed_framework_names
= ['JavaScriptCore', 'WebInspector', 'Test']
177 cli_parser
= optparse
.OptionParser(usage
="usage: %prog [options] PrimaryProtocol.json [SupplementalProtocol.json ...]")
178 cli_parser
.add_option("-o", "--outputDir", help="Directory where generated files should be written.")
179 cli_parser
.add_option("--framework", type="choice", choices
=allowed_framework_names
, help="The framework that the primary specification belongs to.")
180 cli_parser
.add_option("--force", action
="store_true", help="Force output of generated scripts, even if nothing changed.")
181 cli_parser
.add_option("-v", "--debug", action
="store_true", help="Log extra output for debugging the generator itself.")
182 cli_parser
.add_option("-t", "--test", action
="store_true", help="Enable test mode. Use unique output filenames created by prepending the input filename.")
186 arg_options
, arg_values
= cli_parser
.parse_args()
187 if (len(arg_values
) < 1):
188 raise ParseException("At least one plain argument expected")
190 if not arg_options
.outputDir
:
191 raise ParseException("Missing output directory")
193 if arg_options
.debug
:
194 log
.setLevel(logging
.DEBUG
)
197 'primary_specification_filepath': arg_values
[0],
198 'supplemental_specification_filepaths': arg_values
[1:],
199 'output_dirpath': arg_options
.outputDir
,
200 'concatenate_output': arg_options
.test
,
201 'framework_name': arg_options
.framework
,
202 'force_output': arg_options
.force
206 generate_from_specification(**options
)
207 except (ParseException
, TypecheckException
) as e
:
211 raise # Force the build to fail.