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