1 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 """The core of this script is the calc_load_sequence function. This function
5 loads the provided javascript files and figures out their dependencies by
6 reading the tvcm.require statements in each file. This allows us to, for
7 example, have a trio of modules, foo, bar and baz, where foo.js contains:
15 If these three modules are in the current directory, then:
17 calc_load_sequence(['foo'], '.')
19 Will return the correct sequence in which to load these modules based on these
20 dependencies, which is: [Module('baz'), Module('bar'), Module('foo')].
26 from tvcm import module
27 from tvcm import resource_loader
30 def calc_load_sequence(filenames, project):
31 """Given a list of starting javascript files, figure out all the Module
32 objects that need to be loaded to satisfy their dependencies.
34 The javascript files should specify their dependencies in a format that is
35 textually equivalent to tvcm/__init__.js' require syntax, namely:
37 tvcm.require(module1);
38 tvcm.require(module2);
39 tvcm.requireStylesheet(stylesheet);
42 filenames: A list of starting file paths for trace viewer modules.
45 A list of Module objects in the order that they should be loaded.
47 if os.path.join('tvcm', '__init__.js') not in filenames:
48 filenames = list(filenames)
49 filenames.insert(0, os.path.join('tvcm', '__init__.js'))
50 return calc_load_sequence_internal(filenames, project)
53 def calc_load_sequence_internal(filenames, project):
54 """Helper function for calc_load_sequence.
57 filenames: A list of starting file paths for trace viewer modules.
58 project : A tvcm.Project
61 A list of Module objects in the list that they should be loaded.
63 loader = resource_loader.ResourceLoader(project)
64 initial_module_name_indices = {}
65 for filename in filenames:
66 m = loader.load_module(module_filename=filename)
67 if m.name not in initial_module_name_indices:
68 initial_module_name_indices[m.name] = len(initial_module_name_indices)
70 # Find the root modules: ones that have no dependencies. While doing that,
71 # sort the dependent module list so that the computed load order is stable.
72 module_ref_counts = {}
73 for m in loader.loaded_modules.values():
74 m.dependent_modules.sort(lambda x, y: cmp(x.name, y.name))
75 module_ref_counts[m.name] = 0
77 # Count the number of references to each module.
78 def inc_ref_count(name):
79 module_ref_counts[name] = module_ref_counts[name] + 1
80 for m in loader.loaded_modules.values():
81 for dependent_module in m.dependent_modules:
82 inc_ref_count(dependent_module.name)
84 # Root modules are modules with nothing depending on them.
85 root_modules = [loader.loaded_modules[name]
86 for name, ref_count in module_ref_counts.items()
89 # Sort root_modules by the order they were originally requested,
90 # then sort everything else by name.
91 def compare_root_module(x, y):
92 n = len(initial_module_name_indices)
93 iX = initial_module_name_indices.get(x.name, n)
94 iY = initial_module_name_indices.get(y.name, n)
97 return cmp(x.name, y.name)
98 root_modules.sort(compare_root_module)
100 already_loaded_set = set()
102 for m in root_modules:
103 m.compute_load_sequence_recursive(load_sequence, already_loaded_set)