1#! /usr/bin/python -B 2# -*- coding: utf-8 -*- 3# 4# Copyright (C) 2016 and later: Unicode, Inc. and others. 5# License & terms of use: http://www.unicode.org/copyright.html 6# Copyright (C) 2011-2014, International Business Machines 7# Corporation and others. All Rights Reserved. 8# 9# file name: dependencies.py 10# 11# created on: 2011may26 12 13"""Reader module for dependency data for the ICU dependency tester. 14 15Reads dependencies.txt and makes the data available. 16 17Attributes: 18 files: Set of "library/filename.o" files mentioned in the dependencies file. 19 items: Map from library or group names to item maps. 20 Each item has a "type" ("library" or "group" or "system_symbols"). 21 A library or group item can have an optional set of "files" (as in the files attribute). 22 Each item can have an optional set of "deps" (libraries & groups). 23 A group item also has a "library" name unless it is a group of system symbols. 24 The one "system_symbols" item and its groups have sets of "system_symbols" 25 with standard-library system symbol names. 26 libraries: Set of library names mentioned in the dependencies file. 27 file_to_item: Map from a symbol (ushoe.o) to library or group (shoesize) 28""" 29__author__ = "Markus W. Scherer" 30 31# TODO: Support binary items. 32# .txt syntax: binary: tools/genrb 33# item contents: {"type": "binary"} with optional files & deps 34# A binary must not be used as a dependency for anything else. 35 36import sys 37 38files = set() 39items = {} 40libraries = set() 41file_to_item = {} 42 43_line_number = 0 44_groups_to_be_defined = set() 45 46def _CheckLibraryName(name): 47 global _line_number 48 if not name: 49 sys.exit("Error:%d: \"library: \" without name" % _line_number) 50 if name.endswith(".o"): 51 sys.exit("Error:%d: invalid library name %s" % (_line_number, name)) 52 53def _CheckGroupName(name): 54 global _line_number 55 if not name: 56 sys.exit("Error:%d: \"group: \" without name" % _line_number) 57 if "/" in name or name.endswith(".o"): 58 sys.exit("Error:%d: invalid group name %s" % (_line_number, name)) 59 60def _CheckFileName(name): 61 global _line_number 62 if "/" in name or not name.endswith(".o"): 63 sys.exit("Error:%d: invalid file name %s" % (_line_number, name)) 64 65def _RemoveComment(line): 66 global _line_number 67 _line_number = _line_number + 1 68 index = line.find("#") # Remove trailing comment. 69 if index >= 0: line = line[:index] 70 return line.rstrip() # Remove trailing newlines etc. 71 72def _ReadLine(f): 73 while True: 74 line = _RemoveComment(next(f)) 75 if line: return line 76 77def _ReadFiles(deps_file, item, library_name): 78 global files 79 item_files = item.get("files") 80 while True: 81 line = _ReadLine(deps_file) 82 if not line: continue 83 if not line.startswith(" "): return line 84 if item_files == None: item_files = item["files"] = set() 85 for file_name in line.split(): 86 _CheckFileName(file_name) 87 file_name = library_name + "/" + file_name 88 if file_name in files: 89 sys.exit("Error:%d: file %s listed in multiple groups" % (_line_number, file_name)) 90 files.add(file_name) 91 item_files.add(file_name) 92 file_to_item[file_name] = item["name"] 93 94def _IsLibrary(item): return item and item["type"] == "library" 95 96def _IsLibraryGroup(item): return item and "library" in item 97 98def _ReadDeps(deps_file, item, library_name): 99 global items, _line_number, _groups_to_be_defined 100 item_deps = item.get("deps") 101 while True: 102 line = _ReadLine(deps_file) 103 if not line: continue 104 if not line.startswith(" "): return line 105 if item_deps == None: item_deps = item["deps"] = set() 106 for dep in line.split(): 107 _CheckGroupName(dep) 108 dep_item = items.get(dep) 109 if item["type"] == "system_symbols" and (_IsLibraryGroup(dep_item) or _IsLibrary(dep_item)): 110 sys.exit(("Error:%d: system_symbols depend on previously defined " + 111 "library or library group %s") % (_line_number, dep)) 112 if dep_item == None: 113 # Add this dependency as a new group. 114 items[dep] = {"type": "group"} 115 if library_name: items[dep]["library"] = library_name 116 _groups_to_be_defined.add(dep) 117 item_deps.add(dep) 118 119def _AddSystemSymbol(item, symbol): 120 exports = item.get("system_symbols") 121 if exports == None: exports = item["system_symbols"] = set() 122 exports.add(symbol) 123 124def _ReadSystemSymbols(deps_file, item): 125 global _line_number 126 while True: 127 line = _ReadLine(deps_file) 128 if not line: continue 129 if not line.startswith(" "): return line 130 line = line.lstrip() 131 if '"' in line: 132 # One double-quote-enclosed symbol on the line, allows spaces in a symbol name. 133 symbol = line[1:-1] 134 if line.startswith('"') and line.endswith('"') and '"' not in symbol: 135 _AddSystemSymbol(item, symbol) 136 else: 137 sys.exit("Error:%d: invalid quoted symbol name %s" % (_line_number, line)) 138 else: 139 # One or more space-separate symbols. 140 for symbol in line.split(): _AddSystemSymbol(item, symbol) 141 142def Load(): 143 """Reads "dependencies.txt" and populates the module attributes.""" 144 global items, libraries, _line_number, _groups_to_be_defined 145 deps_file = open("dependencies.txt") 146 try: 147 line = None 148 current_type = None 149 while True: 150 while not line: line = _RemoveComment(next(deps_file)) 151 152 if line.startswith("library: "): 153 current_type = "library" 154 name = line[9:].lstrip() 155 _CheckLibraryName(name) 156 if name in items: 157 sys.exit("Error:%d: library definition using duplicate name %s" % (_line_number, name)) 158 libraries.add(name) 159 item = items[name] = {"type": "library", "name": name} 160 line = _ReadFiles(deps_file, item, name) 161 elif line.startswith("group: "): 162 current_type = "group" 163 name = line[7:].lstrip() 164 _CheckGroupName(name) 165 if name not in items: 166 sys.exit("Error:%d: group %s defined before mentioned as a dependency" % 167 (_line_number, name)) 168 if name not in _groups_to_be_defined: 169 sys.exit("Error:%d: group definition using duplicate name %s" % (_line_number, name)) 170 _groups_to_be_defined.remove(name) 171 item = items[name] 172 item["name"] = name 173 library_name = item.get("library") 174 if library_name: 175 line = _ReadFiles(deps_file, item, library_name) 176 else: 177 line = _ReadSystemSymbols(deps_file, item) 178 elif line == " deps": 179 if current_type == "library": 180 line = _ReadDeps(deps_file, items[name], name) 181 elif current_type == "group": 182 item = items[name] 183 line = _ReadDeps(deps_file, item, item.get("library")) 184 elif current_type == "system_symbols": 185 item = items[current_type] 186 line = _ReadDeps(deps_file, item, None) 187 else: 188 sys.exit("Error:%d: deps before any library or group" % _line_number) 189 elif line == "system_symbols:": 190 current_type = "system_symbols" 191 if current_type in items: 192 sys.exit("Error:%d: duplicate entry for system_symbols" % _line_number) 193 item = items[current_type] = {"type": current_type, "name": current_type} 194 line = _ReadSystemSymbols(deps_file, item) 195 else: 196 sys.exit("Syntax error:%d: %s" % (_line_number, line)) 197 except StopIteration: 198 pass 199 if _groups_to_be_defined: 200 sys.exit("Error: some groups mentioned in dependencies are undefined: %s" % _groups_to_be_defined) 201