#! /usr/bin/python -B # -*- coding: utf-8 -*- # # Copyright (C) 2016 and later: Unicode, Inc. and others. # License & terms of use: http://www.unicode.org/copyright.html # Copyright (C) 2011-2014, International Business Machines # Corporation and others. All Rights Reserved. # # file name: dependencies.py # # created on: 2011may26 """Reader module for dependency data for the ICU dependency tester. Reads dependencies.txt and makes the data available. Attributes: files: Set of "library/filename.o" files mentioned in the dependencies file. items: Map from library or group names to item maps. Each item has a "type" ("library" or "group" or "system_symbols"). A library or group item can have an optional set of "files" (as in the files attribute). Each item can have an optional set of "deps" (libraries & groups). A group item also has a "library" name unless it is a group of system symbols. The one "system_symbols" item and its groups have sets of "system_symbols" with standard-library system symbol names. libraries: Set of library names mentioned in the dependencies file. file_to_item: Map from a symbol (ushoe.o) to library or group (shoesize) """ __author__ = "Markus W. Scherer" # TODO: Support binary items. # .txt syntax: binary: tools/genrb # item contents: {"type": "binary"} with optional files & deps # A binary must not be used as a dependency for anything else. import sys files = set() items = {} libraries = set() file_to_item = {} _line_number = 0 _groups_to_be_defined = set() def _CheckLibraryName(name): global _line_number if not name: sys.exit("Error:%d: \"library: \" without name" % _line_number) if name.endswith(".o"): sys.exit("Error:%d: invalid library name %s" % (_line_number, name)) def _CheckGroupName(name): global _line_number if not name: sys.exit("Error:%d: \"group: \" without name" % _line_number) if "/" in name or name.endswith(".o"): sys.exit("Error:%d: invalid group name %s" % (_line_number, name)) def _CheckFileName(name): global _line_number if "/" in name or not name.endswith(".o"): sys.exit("Error:%d: invalid file name %s" % (_line_number, name)) def _RemoveComment(line): global _line_number _line_number = _line_number + 1 index = line.find("#") # Remove trailing comment. if index >= 0: line = line[:index] return line.rstrip() # Remove trailing newlines etc. def _ReadLine(f): while True: line = _RemoveComment(next(f)) if line: return line def _ReadFiles(deps_file, item, library_name): global files item_files = item.get("files") while True: line = _ReadLine(deps_file) if not line: continue if not line.startswith(" "): return line if item_files == None: item_files = item["files"] = set() for file_name in line.split(): _CheckFileName(file_name) file_name = library_name + "/" + file_name if file_name in files: sys.exit("Error:%d: file %s listed in multiple groups" % (_line_number, file_name)) files.add(file_name) item_files.add(file_name) file_to_item[file_name] = item["name"] def _IsLibrary(item): return item and item["type"] == "library" def _IsLibraryGroup(item): return item and "library" in item def _ReadDeps(deps_file, item, library_name): global items, _line_number, _groups_to_be_defined item_deps = item.get("deps") while True: line = _ReadLine(deps_file) if not line: continue if not line.startswith(" "): return line if item_deps == None: item_deps = item["deps"] = set() for dep in line.split(): _CheckGroupName(dep) dep_item = items.get(dep) if item["type"] == "system_symbols" and (_IsLibraryGroup(dep_item) or _IsLibrary(dep_item)): sys.exit(("Error:%d: system_symbols depend on previously defined " + "library or library group %s") % (_line_number, dep)) if dep_item == None: # Add this dependency as a new group. items[dep] = {"type": "group"} if library_name: items[dep]["library"] = library_name _groups_to_be_defined.add(dep) item_deps.add(dep) def _AddSystemSymbol(item, symbol): exports = item.get("system_symbols") if exports == None: exports = item["system_symbols"] = set() exports.add(symbol) def _ReadSystemSymbols(deps_file, item): global _line_number while True: line = _ReadLine(deps_file) if not line: continue if not line.startswith(" "): return line line = line.lstrip() if '"' in line: # One double-quote-enclosed symbol on the line, allows spaces in a symbol name. symbol = line[1:-1] if line.startswith('"') and line.endswith('"') and '"' not in symbol: _AddSystemSymbol(item, symbol) else: sys.exit("Error:%d: invalid quoted symbol name %s" % (_line_number, line)) else: # One or more space-separate symbols. for symbol in line.split(): _AddSystemSymbol(item, symbol) def Load(): """Reads "dependencies.txt" and populates the module attributes.""" global items, libraries, _line_number, _groups_to_be_defined deps_file = open("dependencies.txt") try: line = None current_type = None while True: while not line: line = _RemoveComment(next(deps_file)) if line.startswith("library: "): current_type = "library" name = line[9:].lstrip() _CheckLibraryName(name) if name in items: sys.exit("Error:%d: library definition using duplicate name %s" % (_line_number, name)) libraries.add(name) item = items[name] = {"type": "library", "name": name} line = _ReadFiles(deps_file, item, name) elif line.startswith("group: "): current_type = "group" name = line[7:].lstrip() _CheckGroupName(name) if name not in items: sys.exit("Error:%d: group %s defined before mentioned as a dependency" % (_line_number, name)) if name not in _groups_to_be_defined: sys.exit("Error:%d: group definition using duplicate name %s" % (_line_number, name)) _groups_to_be_defined.remove(name) item = items[name] item["name"] = name library_name = item.get("library") if library_name: line = _ReadFiles(deps_file, item, library_name) else: line = _ReadSystemSymbols(deps_file, item) elif line == " deps": if current_type == "library": line = _ReadDeps(deps_file, items[name], name) elif current_type == "group": item = items[name] line = _ReadDeps(deps_file, item, item.get("library")) elif current_type == "system_symbols": item = items[current_type] line = _ReadDeps(deps_file, item, None) else: sys.exit("Error:%d: deps before any library or group" % _line_number) elif line == "system_symbols:": current_type = "system_symbols" if current_type in items: sys.exit("Error:%d: duplicate entry for system_symbols" % _line_number) item = items[current_type] = {"type": current_type, "name": current_type} line = _ReadSystemSymbols(deps_file, item) else: sys.exit("Syntax error:%d: %s" % (_line_number, line)) except StopIteration: pass if _groups_to_be_defined: sys.exit("Error: some groups mentioned in dependencies are undefined: %s" % _groups_to_be_defined)