blob: c12f669489596b21f00e2a8e470f627e01fb2dac [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:37 +00001#!/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"""
7Utilities for the modular DevTools build.
8"""
9
10import collections
11from os import path
12import os
13
14try:
15 import simplejson as json
16except ImportError:
17 import json
18
19
20def read_file(filename):
21 with open(path.normpath(filename), 'rt') as input:
22 return input.read()
23
24
25def write_file(filename, content):
26 if path.exists(filename):
27 os.remove(filename)
28 directory = path.dirname(filename)
29 if not path.exists(directory):
30 os.makedirs(directory)
31 with open(filename, 'wt') as output:
32 output.write(content)
33
34
35def bail_error(message):
36 raise Exception(message)
37
38
39def load_and_parse_json(filename):
40 try:
41 return json.loads(read_file(filename))
42 except:
43 print 'ERROR: Failed to parse %s' % filename
44 raise
45
46
47def concatenate_scripts(file_names, module_dir, output_dir, output):
48 for file_name in file_names:
49 output.write('/* %s */\n' % file_name)
50 file_path = path.join(module_dir, file_name)
51 if not path.isfile(file_path):
52 file_path = path.join(output_dir, path.basename(module_dir), file_name)
53 output.write(read_file(file_path))
54 output.write(';')
55
56
57class Descriptors:
58
59 def __init__(self, application_name, application_dir, application_descriptor, module_descriptors, extends, has_html):
60 self.application_name = application_name
61 self.application_dir = application_dir
62 self.application = application_descriptor
63 self._cached_sorted_modules = None
64 self.modules = module_descriptors
65 self.extends = extends
66 self.has_html = has_html
67
68 def application_json(self):
69 result = dict()
70 result['modules'] = self.application.values()
71 result['has_html'] = self.has_html
72 return json.dumps(result)
73
74 def all_compiled_files(self):
75 files = collections.OrderedDict()
76 for name in self.sorted_modules():
77 module = self.modules[name]
78 skipped_files = set(module.get('skip_compilation', []))
Tim van der Lippe790b9292019-09-19 15:14:16 +000079 for script in module.get('scripts', []) + module.get('modules', []):
Blink Reformat4c46d092018-04-07 15:32:37 +000080 if script not in skipped_files:
81 files[path.normpath(path.join(self.application_dir, name, script))] = True
82 return files.keys()
83
84 def module_compiled_files(self, name):
85 files = []
86 module = self.modules.get(name)
87 skipped_files = set(module.get('skip_compilation', []))
88 for script in module.get('scripts', []):
89 if script not in skipped_files:
90 files.append(script)
91 return files
92
93 def module_resources(self, name):
94 return [name + '/' + resource for resource in self.modules[name].get('resources', [])]
95
96 def sorted_modules(self):
97 if self._cached_sorted_modules:
98 return self._cached_sorted_modules
99
100 result = []
101 unvisited_modules = set(self.modules)
102 temp_modules = set()
103
104 def visit(parent, name):
105 if name not in unvisited_modules:
106 return None
107 if name not in self.modules:
108 return (parent, name)
109 if name in temp_modules:
110 bail_error('Dependency cycle found at module "%s"' % name)
111 temp_modules.add(name)
112 deps = self.modules[name].get('dependencies')
113 if deps:
114 for dep_name in deps:
115 bad_dep = visit(name, dep_name)
116 if bad_dep:
117 return bad_dep
118 unvisited_modules.remove(name)
119 temp_modules.remove(name)
120 result.append(name)
121 return None
122
123 while len(unvisited_modules):
124 for next in unvisited_modules:
125 break
126 failure = visit(None, next)
127 if failure:
128 # failure[0] can never be None
129 bail_error('Unknown module "%s" encountered in dependencies of "%s"' % (failure[1], failure[0]))
130
131 self._cached_sorted_modules = result
132 return result
133
134 def sorted_dependencies_closure(self, module_name):
135 visited = set()
136
137 def sorted_deps_for_module(name):
138 result = []
139 desc = self.modules[name]
140 deps = desc.get('dependencies', [])
141 for dep in deps:
142 result += sorted_deps_for_module(dep)
143 if name not in visited:
144 result.append(name)
145 visited.add(name)
146 return result
147
148 return sorted_deps_for_module(module_name)
149
150
151class DescriptorLoader:
152
153 def __init__(self, application_dir):
154 self.application_dir = application_dir
155
156 def load_application(self, application_descriptor_name):
157 all_module_descriptors = {}
158 result = self._load_application(application_descriptor_name, all_module_descriptors)
159 return result
160
161 def load_applications(self, application_descriptor_names):
162 all_module_descriptors = {}
163 all_application_descriptors = {}
164 for application_descriptor_name in application_descriptor_names:
165 descriptors = {}
166 result = self._load_application(application_descriptor_name, descriptors)
167 for name in descriptors:
168 all_module_descriptors[name] = descriptors[name]
169 for name in result.application:
170 all_application_descriptors[name] = result.application[name]
171 return Descriptors('all', self.application_dir, all_application_descriptors, all_module_descriptors, None, False)
172
173 def _load_application(self, application_descriptor_name, all_module_descriptors):
174 module_descriptors = {}
175 application_descriptor_filename = path.join(self.application_dir, application_descriptor_name + '.json')
176 descriptor_json = load_and_parse_json(application_descriptor_filename)
177 application_descriptor = {desc['name']: desc for desc in descriptor_json['modules']}
178 extends = descriptor_json['extends'] if 'extends' in descriptor_json else None
179 if extends:
180 extends = self._load_application(extends, all_module_descriptors)
181 has_html = True if 'has_html' in descriptor_json and descriptor_json['has_html'] else False
182
183 for (module_name, module) in application_descriptor.items():
184 if all_module_descriptors.get(module_name):
185 bail_error('Duplicate definition of module "%s" in %s' % (module_name, application_descriptor_filename))
186 module_descriptors[module_name] = self._read_module_descriptor(module_name, application_descriptor_filename)
187 all_module_descriptors[module_name] = module_descriptors[module_name]
188
189 for module in module_descriptors.values():
190 for dep in module.get('dependencies', []):
191 if dep not in all_module_descriptors:
192 bail_error('Module "%s" (dependency of "%s") not listed in application descriptor %s' %
193 (dep, module['name'], application_descriptor_filename))
194
Yang Guo4fd355c2019-09-19 10:59:03 +0200195 return Descriptors(application_descriptor_name, self.application_dir, application_descriptor, module_descriptors, extends,
196 has_html)
Blink Reformat4c46d092018-04-07 15:32:37 +0000197
198 def _read_module_descriptor(self, module_name, application_descriptor_filename):
199 json_filename = path.join(self.application_dir, module_name, 'module.json')
200 if not path.exists(json_filename):
201 bail_error('Module descriptor %s referenced in %s is missing' % (json_filename, application_descriptor_filename))
202 module_json = load_and_parse_json(json_filename)
203 module_json['name'] = module_name
204 return module_json