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