| 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 |