]>
Commit | Line | Data |
---|---|---|
ed1e77d3 A |
1 | #!/usr/bin/env python |
2 | # | |
3 | # Copyright (c) 2014 Apple Inc. All rights reserved. | |
4 | # Copyright (c) 2014 University of Washington. All rights reserved. | |
5 | # | |
6 | # Redistribution and use in source and binary forms, with or without | |
7 | # modification, are permitted provided that the following conditions | |
8 | # are met: | |
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. | |
14 | # | |
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. | |
26 | ||
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. | |
29 | ||
30 | import os.path | |
31 | import re | |
32 | import sys | |
33 | import string | |
34 | from string import Template | |
35 | import optparse | |
36 | import logging | |
37 | ||
38 | try: | |
39 | import json | |
40 | except ImportError: | |
41 | import simplejson as json | |
42 | ||
43 | logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR) | |
44 | log = logging.getLogger('global') | |
45 | ||
46 | try: | |
47 | from codegen import * | |
48 | ||
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. | |
53 | import models | |
54 | from models import * | |
55 | from generator import * | |
56 | from cpp_generator import * | |
57 | from objc_generator import * | |
58 | ||
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 * | |
76 | ||
77 | ||
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 | |
82 | self._output = "" | |
83 | self.force_output = force_output | |
84 | ||
85 | def write(self, text): | |
86 | self._output += text | |
87 | ||
88 | def close(self): | |
89 | text_changed = True | |
90 | self._output = self._output.rstrip() + "\n" | |
91 | ||
92 | try: | |
93 | if self.force_output: | |
94 | raise | |
95 | ||
96 | read_file = open(self._filepath, "r") | |
97 | old_text = read_file.read() | |
98 | read_file.close() | |
99 | text_changed = old_text != self._output | |
100 | except: | |
101 | # Ignore, just overwrite by default | |
102 | pass | |
103 | ||
104 | if text_changed or self.force_output: | |
105 | out_file = open(self._filepath, "w") | |
106 | out_file.write(self._output) | |
107 | out_file.close() | |
108 | ||
109 | ||
110 | def generate_from_specification(primary_specification_filepath=None, | |
111 | supplemental_specification_filepaths=[], | |
112 | concatenate_output=False, | |
113 | output_dirpath=None, | |
114 | force_output=False, | |
115 | framework_name=""): | |
116 | ||
117 | def load_specification(protocol, filepath, isSupplemental=False): | |
118 | try: | |
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) | |
124 | ||
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) | |
129 | ||
130 | protocol.resolve_types() | |
131 | ||
132 | generators = [] | |
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)) | |
153 | ||
154 | single_output_file_contents = [] | |
155 | ||
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('') | |
163 | else: | |
164 | output_file = IncrementalFileWriter(os.path.join(output_dirpath, generator.output_filename()), force_output) | |
165 | output_file.write(output) | |
166 | output_file.close() | |
167 | ||
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)) | |
172 | output_file.close() | |
173 | ||
174 | ||
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.") | |
183 | ||
184 | options = None | |
185 | ||
186 | arg_options, arg_values = cli_parser.parse_args() | |
187 | if (len(arg_values) < 1): | |
188 | raise ParseException("At least one plain argument expected") | |
189 | ||
190 | if not arg_options.outputDir: | |
191 | raise ParseException("Missing output directory") | |
192 | ||
193 | if arg_options.debug: | |
194 | log.setLevel(logging.DEBUG) | |
195 | ||
196 | options = { | |
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 | |
203 | } | |
204 | ||
205 | try: | |
206 | generate_from_specification(**options) | |
207 | except (ParseException, TypecheckException) as e: | |
208 | if arg_options.test: | |
209 | log.error(e.message) | |
210 | else: | |
211 | raise # Force the build to fail. |