OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # | |
3 # Copyright 2014 The Chromium Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 """ | |
8 Release: | |
9 - Concatenates autostart modules, application modules' module.json descriptors
, | |
10 and the application loader into a single script. | |
11 - Builds app.html referencing the application script. | |
12 Debug: | |
13 - Copies the module directories into their destinations. | |
14 - Copies app.html as-is. | |
15 """ | |
16 | |
17 from cStringIO import StringIO | |
18 from os import path | |
19 from os.path import join | |
20 from modular_build import read_file, write_file, bail_error | |
21 import copy | |
22 import modular_build | |
23 import os | |
24 import re | |
25 import shutil | |
26 import sys | |
27 | |
28 try: | |
29 import simplejson as json | |
30 except ImportError: | |
31 import json | |
32 | |
33 import rjsmin | |
34 | |
35 | |
36 def resource_source_url(url): | |
37 return '\n/*# sourceURL=' + url + ' */' | |
38 | |
39 | |
40 def minify_js(javascript): | |
41 return rjsmin.jsmin(javascript) | |
42 | |
43 | |
44 def concatenated_module_filename(module_name, output_dir): | |
45 return join(output_dir, module_name + '/' + module_name + '_module.js') | |
46 | |
47 | |
48 def symlink_or_copy_file(src, dest, safe=False): | |
49 if safe and path.exists(dest): | |
50 os.remove(dest) | |
51 if hasattr(os, 'symlink'): | |
52 os.symlink(src, dest) | |
53 else: | |
54 shutil.copy(src, dest) | |
55 | |
56 | |
57 def symlink_or_copy_dir(src, dest): | |
58 if path.exists(dest): | |
59 shutil.rmtree(dest) | |
60 for src_dir, dirs, files in os.walk(src): | |
61 subpath = path.relpath(src_dir, src) | |
62 dest_dir = path.normpath(join(dest, subpath)) | |
63 os.mkdir(dest_dir) | |
64 for name in files: | |
65 src_name = join(os.getcwd(), src_dir, name) | |
66 dest_name = join(dest_dir, name) | |
67 symlink_or_copy_file(src_name, dest_name) | |
68 | |
69 | |
70 class AppBuilder: | |
71 def __init__(self, application_name, descriptors, application_dir, output_di
r): | |
72 self.application_name = application_name | |
73 self.descriptors = descriptors | |
74 self.application_dir = application_dir | |
75 self.output_dir = output_dir | |
76 | |
77 def app_file(self, extension): | |
78 return self.application_name + '.' + extension | |
79 | |
80 def core_resource_names(self): | |
81 result = [] | |
82 for module in self.descriptors.sorted_modules(): | |
83 if self.descriptors.application[module].get('type') != 'autostart': | |
84 continue | |
85 | |
86 resources = self.descriptors.modules[module].get('resources') | |
87 if not resources: | |
88 continue | |
89 for resource_name in resources: | |
90 result.append(path.join(module, resource_name)) | |
91 return result | |
92 | |
93 | |
94 # Outputs: | |
95 # <app_name>.html | |
96 # <app_name>.js | |
97 # <module_name>_module.js | |
98 class ReleaseBuilder(AppBuilder): | |
99 def __init__(self, application_name, descriptors, application_dir, output_di
r): | |
100 AppBuilder.__init__(self, application_name, descriptors, application_dir
, output_dir) | |
101 | |
102 def build_app(self): | |
103 if self.descriptors.has_html: | |
104 self._build_html() | |
105 self._build_app_script() | |
106 for module in filter(lambda desc: (not desc.get('type') or desc.get('typ
e') == 'remote'), self.descriptors.application.values()): | |
107 self._concatenate_dynamic_module(module['name']) | |
108 | |
109 def _build_html(self): | |
110 html_name = self.app_file('html') | |
111 output = StringIO() | |
112 with open(join(self.application_dir, html_name), 'r') as app_input_html: | |
113 for line in app_input_html: | |
114 if '<script ' in line or '<link ' in line: | |
115 continue | |
116 if '</head>' in line: | |
117 output.write(self._generate_include_tag(self.app_file('js'))
) | |
118 output.write(line) | |
119 | |
120 write_file(join(self.output_dir, html_name), output.getvalue()) | |
121 output.close() | |
122 | |
123 def _build_app_script(self): | |
124 script_name = self.app_file('js') | |
125 output = StringIO() | |
126 self._concatenate_application_script(output) | |
127 write_file(join(self.output_dir, script_name), minify_js(output.getvalue
())) | |
128 output.close() | |
129 | |
130 def _generate_include_tag(self, resource_path): | |
131 if (resource_path.endswith('.js')): | |
132 return ' <script type="text/javascript" src="%s"></script>\n' % r
esource_path | |
133 else: | |
134 assert resource_path | |
135 | |
136 def _release_module_descriptors(self): | |
137 module_descriptors = self.descriptors.modules | |
138 result = [] | |
139 for name in module_descriptors: | |
140 module = copy.copy(module_descriptors[name]) | |
141 module_type = self.descriptors.application[name].get('type') | |
142 # Clear scripts, as they are not used at runtime | |
143 # (only the fact of their presence is important). | |
144 resources = module.get('resources', None) | |
145 if module.get('scripts') or resources: | |
146 if module_type == 'autostart': | |
147 # Autostart modules are already baked in. | |
148 del module['scripts'] | |
149 else: | |
150 # Non-autostart modules are vulcanized. | |
151 module['scripts'] = [name + '_module.js'] | |
152 # Resources are already baked into scripts. | |
153 if resources is not None: | |
154 del module['resources'] | |
155 result.append(module) | |
156 return json.dumps(result) | |
157 | |
158 def _write_module_resources(self, resource_names, output): | |
159 for resource_name in resource_names: | |
160 resource_name = path.normpath(resource_name).replace('\\', '/') | |
161 output.write('Runtime.cachedResources["%s"] = "' % resource_name) | |
162 resource_content = read_file(path.join(self.application_dir, resourc
e_name)) + resource_source_url(resource_name) | |
163 resource_content = resource_content.replace('\\', '\\\\') | |
164 resource_content = resource_content.replace('\n', '\\n') | |
165 resource_content = resource_content.replace('"', '\\"') | |
166 output.write(resource_content) | |
167 output.write('";\n') | |
168 | |
169 def _concatenate_autostart_modules(self, output): | |
170 non_autostart = set() | |
171 sorted_module_names = self.descriptors.sorted_modules() | |
172 for name in sorted_module_names: | |
173 desc = self.descriptors.modules[name] | |
174 name = desc['name'] | |
175 type = self.descriptors.application[name].get('type') | |
176 if type == 'autostart': | |
177 deps = set(desc.get('dependencies', [])) | |
178 non_autostart_deps = deps & non_autostart | |
179 if len(non_autostart_deps): | |
180 bail_error('Non-autostart dependencies specified for the aut
ostarted module "%s": %s' % (name, non_autostart_deps)) | |
181 output.write('\n/* Module %s */\n' % name) | |
182 modular_build.concatenate_scripts(desc.get('scripts'), join(self
.application_dir, name), self.output_dir, output) | |
183 else: | |
184 non_autostart.add(name) | |
185 | |
186 def _concatenate_application_script(self, output): | |
187 runtime_contents = read_file(join(self.application_dir, 'Runtime.js')) | |
188 runtime_contents = re.sub('var allDescriptors = \[\];', 'var allDescript
ors = %s;' % self._release_module_descriptors().replace('\\', '\\\\'), runtime_c
ontents, 1) | |
189 output.write('/* Runtime.js */\n') | |
190 output.write(runtime_contents) | |
191 output.write('\n/* Autostart modules */\n') | |
192 self._concatenate_autostart_modules(output) | |
193 output.write('/* Application descriptor %s */\n' % self.app_file('json')
) | |
194 output.write('applicationDescriptor = ') | |
195 output.write(self.descriptors.application_json()) | |
196 output.write(';\n/* Core resources */\n') | |
197 self._write_module_resources(self.core_resource_names(), output) | |
198 output.write('\n/* Application loader */\n') | |
199 output.write(read_file(join(self.application_dir, self.app_file('js')))) | |
200 | |
201 def _concatenate_dynamic_module(self, module_name): | |
202 module = self.descriptors.modules[module_name] | |
203 scripts = module.get('scripts') | |
204 resources = self.descriptors.module_resources(module_name) | |
205 module_dir = join(self.application_dir, module_name) | |
206 output = StringIO() | |
207 if scripts: | |
208 modular_build.concatenate_scripts(scripts, module_dir, self.output_d
ir, output) | |
209 if resources: | |
210 self._write_module_resources(resources, output) | |
211 output_file_path = concatenated_module_filename(module_name, self.output
_dir) | |
212 write_file(output_file_path, minify_js(output.getvalue())) | |
213 output.close() | |
214 | |
215 | |
216 # Outputs: | |
217 # <app_name>.html as-is | |
218 # <app_name>.js as-is | |
219 # <module_name>/<all_files> | |
220 class DebugBuilder(AppBuilder): | |
221 def __init__(self, application_name, descriptors, application_dir, output_di
r): | |
222 AppBuilder.__init__(self, application_name, descriptors, application_dir
, output_dir) | |
223 | |
224 def build_app(self): | |
225 if self.descriptors.has_html: | |
226 self._build_html() | |
227 js_name = self.app_file('js') | |
228 src_name = join(os.getcwd(), self.application_dir, js_name) | |
229 symlink_or_copy_file(src_name, join(self.output_dir, js_name), True) | |
230 for module_name in self.descriptors.modules: | |
231 module = self.descriptors.modules[module_name] | |
232 input_module_dir = join(self.application_dir, module_name) | |
233 output_module_dir = join(self.output_dir, module_name) | |
234 symlink_or_copy_dir(input_module_dir, output_module_dir) | |
235 | |
236 def _build_html(self): | |
237 html_name = self.app_file('html') | |
238 symlink_or_copy_file(join(os.getcwd(), self.application_dir, html_name),
join(self.output_dir, html_name), True) | |
239 | |
240 | |
241 def build_application(application_name, loader, application_dir, output_dir, rel
ease_mode): | |
242 descriptors = loader.load_application(application_name + '.json') | |
243 if release_mode: | |
244 builder = ReleaseBuilder(application_name, descriptors, application_dir,
output_dir) | |
245 else: | |
246 builder = DebugBuilder(application_name, descriptors, application_dir, o
utput_dir) | |
247 builder.build_app() | |
OLD | NEW |