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 Utilities for the modular DevTools build. 9 """ 10 11 from os import path 12 import os 13 14 try: 15 import simplejson as json 16 except ImportError: 17 import json 18 19 20 def read_file(filename): 21 with open(path.normpath(filename), 'rt') as input: 22 return input.read() 23 24 25 def write_file(filename, content): 26 if path.exists(filename): 27 os.remove(filename) 28 with open(filename, 'wt') as output: 29 output.write(content) 30 31 32 def bail_error(message): 33 raise Exception(message) 34 35 36 def concatenate_scripts(file_names, module_dir, output_dir, output): 37 for file_name in file_names: 38 output.write('/* %s */\n' % file_name) 39 file_path = path.join(module_dir, file_name) 40 if not path.isfile(file_path): 41 file_path = path.join(output_dir, path.basename(module_dir), file_name) 42 output.write(read_file(file_path)) 43 output.write(';') 44 45 46 class Descriptors: 47 def __init__(self, application_dir, application_descriptor, module_descriptors, application_json): 48 self.application_dir = application_dir 49 self.application = application_descriptor 50 self.modules = module_descriptors 51 self.application_json = application_json 52 53 def all_compiled_files(self): 54 files = {} 55 for name in self.modules: 56 module = self.modules[name] 57 skipped_files = set(module.get('skip_compilation', [])) 58 for script in module.get('scripts', []): 59 if script not in skipped_files: 60 files[path.normpath(path.join(self.application_dir, name, script))] = True 61 return files.keys() 62 63 def module_compiled_files(self, name): 64 files = [] 65 module = self.modules[name] 66 skipped_files = set(module.get('skip_compilation', [])) 67 for script in module.get('scripts', []): 68 if script not in skipped_files: 69 files.append(script) 70 return files 71 72 def sorted_modules(self): 73 result = [] 74 unvisited_modules = set(self.modules) 75 temp_modules = set() 76 77 def visit(parent, name): 78 if name not in unvisited_modules: 79 return None 80 if name not in self.modules: 81 return (parent, name) 82 if name in temp_modules: 83 bail_error('Dependency cycle found at module "%s"' % name) 84 temp_modules.add(name) 85 deps = self.modules[name].get('dependencies') 86 if deps: 87 for dep_name in deps: 88 bad_dep = visit(name, dep_name) 89 if bad_dep: 90 return bad_dep 91 unvisited_modules.remove(name) 92 temp_modules.remove(name) 93 result.append(name) 94 return None 95 96 while len(unvisited_modules): 97 for next in unvisited_modules: 98 break 99 failure = visit(None, next) 100 if failure: 101 # failure[0] can never be None 102 bail_error('Unknown module "%s" encountered in dependencies of "%s"' % (failure[1], failure[0])) 103 104 return result 105 106 def sorted_dependencies_closure(self, module_name): 107 visited = set() 108 109 def sorted_deps_for_module(name): 110 result = [] 111 desc = self.modules[name] 112 deps = desc.get('dependencies', []) 113 for dep in deps: 114 result += sorted_deps_for_module(dep) 115 if name not in visited: 116 result.append(name) 117 visited.add(name) 118 return result 119 120 return sorted_deps_for_module(module_name) 121 122 123 class DescriptorLoader: 124 def __init__(self, application_dir): 125 self.application_dir = application_dir 126 127 def load_application(self, application_descriptor_name): 128 application_descriptor_filename = path.join(self.application_dir, application_descriptor_name) 129 application_descriptor_json = read_file(application_descriptor_filename) 130 application_descriptor = {desc['name']: desc for desc in json.loads(application_descriptor_json)} 131 132 module_descriptors = {} 133 for (module_name, module) in application_descriptor.items(): 134 if module_descriptors.get(module_name): 135 bail_error('Duplicate definition of module "%s" in %s' % (module_name, application_descriptor_filename)) 136 module_json_filename = path.join(self.application_dir, module_name, 'module.json') 137 module_descriptors[module_name] = self._read_module_descriptor(module_name, application_descriptor_filename) 138 139 for module in module_descriptors.values(): 140 deps = module.get('dependencies', []) 141 for dep in deps: 142 if dep not in application_descriptor: 143 bail_error('Module "%s" (dependency of "%s") not listed in application descriptor %s' % (dep, module['name'], application_descriptor_filename)) 144 return Descriptors(self.application_dir, application_descriptor, module_descriptors, application_descriptor_json) 145 146 def _read_module_descriptor(self, module_name, application_descriptor_filename): 147 json_filename = path.join(self.application_dir, module_name, 'module.json') 148 if not path.exists(json_filename): 149 bail_error('Module descriptor %s referenced in %s is missing' % (json_filename, application_descriptor_filename)) 150 module_json = json.loads(read_file(json_filename)) 151 module_json['name'] = module_name 152 return module_json 153