diff options
author | Kohei Yoshida <kyoshida@novell.com> | 2011-07-25 22:41:01 -0400 |
---|---|---|
committer | Kohei Yoshida <kyoshida@novell.com> | 2011-07-25 22:41:47 -0400 |
commit | 20d3758189a41abdb88ca463f4ad755b547f2027 (patch) | |
tree | 613343e57eedf692183a51c003d7e4a64d36ac8b | |
parent | 183e9062e423ea2289645295cc5c48c3618ad348 (diff) |
Build analysis scripts moved to contrib/dev-tools.
-rwxr-xr-x | scratch/build-analysis-tools/check-deps.py | 387 | ||||
-rwxr-xr-x | scratch/build-analysis-tools/parse-scp2.py | 855 |
2 files changed, 0 insertions, 1242 deletions
diff --git a/scratch/build-analysis-tools/check-deps.py b/scratch/build-analysis-tools/check-deps.py deleted file mode 100755 index c4256d60e..000000000 --- a/scratch/build-analysis-tools/check-deps.py +++ /dev/null @@ -1,387 +0,0 @@ -#!/usr/bin/env python -######################################################################## -# -# Copyright (c) 2010 Kohei Yoshida -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, -# copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following -# conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. -# -######################################################################## - -import sys, os, os.path, optparse, subprocess - -class ParseError(Exception): pass - -class SingleModeError(Exception): pass - -class ViewerError(Exception): pass - -arg_desc = "module1 module2 ..." - -desc = """ -Execute this script at the root directory of your OOo build tree. It parses -all build.lst files found in the modules and outputs module dependency data -in the dot compatible format. - -When no arguments are given, it prints dependencies of all discovered -modules. When module names are given as arguments, it only traces -dependencies of those modules. - -Sometimes modules are referenced in the build.lst but are absent from the -source tree. Those missing modules are displayed red in the dependency graph.""" - -err_missing_modules = """ -The following modules are mentioned but not present in the source tree:""" - -class Module(object): - - def __init__ (self, name): - self.name = name - self.deps = {} # dependents - self.precs = {} # precedents - -# Store all unique dependency set, with no duplicates. -class DependSet(object): - - def __init__ (self): - self.modules = {} - - def insert_depend (self, prec, dep): - if not self.modules.has_key(prec): - self.modules[prec] = {} - if dep != None: - self.modules[prec][dep] = True - -class DepsCheker(object): - - def __init__ (self): - self.modules = {} # all mentioned modules, whether present or not. - self.modules_present = {} # modules actually present in the source tree. - self.modules_used = {} # modules displayed in the graph. - self.selected = [] # selected modules from the command line args. - - self.modules_missing = None - - def __normalize_name (self, name): - # Replace prohibited characters with someone sane. - name = name.replace('-', '_') - return name - - def __insert_depend (self, mod, dep): - - # precedent to dependent - if not self.modules.has_key(mod): - self.modules[mod] = Module(mod) - obj = self.modules[mod] - obj.deps[dep] = True - - # dependent to precedent - if not self.modules.has_key(dep): - self.modules[dep] = Module(dep) - obj = self.modules[dep] - obj.precs[mod] = True - - def __parse_build_lst (self, build_lst): - - # Read only the first line - file = open(build_lst, 'r') - while True: - line = file.readline().strip() - if line[0] != '#': - break - file.close() - - words = line.split() - n = len(words) - - # Check line format to make sure it's formatted as expected. - if n < 4: - raise ParseError() - if words[2] != ':' and words[2] != '::': - raise ParseError() - if words[-1] != 'NULL': - raise ParseError() - - mod_name = self.__normalize_name(words[1]) - depends = words[3:] - for dep in depends: - if dep == 'NULL': - break - - names = dep.split(':') - if len(names) > 2: - raise ParseError() - elif len(names) == 2: - dep = names[1] - - dep = self.__normalize_name(dep) - self.__insert_depend(mod_name, dep) - - def run (self, selected): - - # modules we want to print dependency on. - self.selected = selected - - # Find all build.lst files. - for mod in os.listdir(os.getcwd()): - if not os.path.isdir(mod): - # not a directory - continue - - build_lst = mod + '/prj/build.lst' - if not os.path.isfile(build_lst): - # no build.lst found - continue - - self.modules_present[self.__normalize_name(mod)] = True - self.__parse_build_lst(build_lst) - - def __build_depset_all (self): - self.dep_set = DependSet() # reset - if len(self.selected) == 0: - mods = self.modules.keys() - for mod in mods: - deps = self.modules[mod].deps.keys() - for dep in deps: - self.dep_set.insert_depend(mod, dep) - else: - # determine involved modules. - self.__processed_mods = {} - for selected in self.selected: - if not self.modules.has_key(selected): - raise ParseError() - - if len(self.modules[selected].deps) > 0: - self.__trace_deps(self.modules[selected]) - - def __build_depset_single (self, mods): - self.dep_set = DependSet() # reset - for mod in mods: - - if not self.modules.has_key(mod): - continue - - obj = self.modules[mod] - if len(obj.precs) == 0 and len(obj.deps) == 0: - # No dependencies. Just print the module. - self.dep_set.insert_depend(mod, None) - continue - - for prec in obj.precs.keys(): - self.dep_set.insert_depend(prec, obj.name) - for dep in obj.deps.keys(): - self.dep_set.insert_depend(obj.name, dep) - - def print_dot_all (self): - self.__build_depset_all() - s = "digraph modules {\n" - s += self.__print_dot_depset() - s += self.__print_dot_selected() - s += self.__print_dot_missing_modules() - s += "}\n" - return s - - def print_dot_single (self, mods): - self.__build_depset_single(mods) - s = "digraph modules {\n" - s += self.__print_dot_depset() - s += self.__print_dot_selected() - s += self.__print_dot_missing_modules() - s += "}\n" - return s - - def print_flat_all (self): - self.__build_depset_all() - return self.__print_flat_depset() - - def print_flat_single (self, mods): - self.__build_depset_single(mods) - return self.__print_flat_depset() - - def __calc_missing_modules (self): - if self.modules_missing != None: - # already calculated. - return - - present = self.modules_present.keys() - self.modules_missing = {} - for mod in self.modules.keys(): - if not self.modules_present.has_key(mod): - self.modules_missing[mod] = True - - def print_missing_modules (self): - self.__calc_missing_modules() - - if len(self.modules_missing) == 0: - return - - sys.stderr.write(err_missing_modules + "\n") - keys = self.modules_missing.keys() - keys.sort() - for mod in keys: - sys.stderr.write(" " + mod + "\n") - - def __trace_deps (self, obj): - if self.__processed_mods.has_key(obj.name): - return - - self.__processed_mods[obj.name] = True - - for dep_name in obj.deps.keys(): - if not self.modules.has_key(dep_name): - raise ParseError() - self.dep_set.insert_depend(obj.name, dep_name) - self.__trace_deps(self.modules[dep_name]) - - def __print_flat_depset (self): - s = '' - mods = self.dep_set.modules.keys() - mods.sort() - for mod in mods: - deps = self.dep_set.modules[mod].keys() - if len(deps) == 0: - # this module has no dependency. - s += "%s\n"%mod - else: - deps.sort() - for dep in deps: - s += "%s:%s\n"%(mod, dep) - return s - - def __print_dot_depset (self): - s = '' - mods = self.dep_set.modules.keys() - for mod in mods: - deps = self.dep_set.modules[mod].keys() - if len(deps) == 0: - # this module has no dependency. - s += self.__print_dot_dep_line(mod, None) - else: - for dep in deps: - s += self.__print_dot_dep_line(mod, dep) - return s - - def __print_dot_selected (self): - s = '' - for mod in self.selected: - if not self.modules_used.has_key(mod): - continue - s += " %s [color=lightblue,style=filled];\n"%mod - return s - - - def __print_dot_missing_modules (self): - self.__calc_missing_modules() - s = '' - for mod in self.modules_missing.keys(): - if not self.modules_used.has_key(mod): - continue - s += " %s [color=red,style=filled];\n"%mod - - return s - - - def __print_dot_dep_line (self, prec, dep): - if prec == None: - raise ParseError() - - self.modules_used[prec] = True - if dep == None: - # this module has no dependency. I still need to mention the module name. - return " %s;\n"%prec - self.modules_used[dep] = True - return " %s -> %s;\n"%(prec, dep) - -def exec_exists (cmd): - retcode = subprocess.call("which %s >/dev/null 2>/dev/null"%cmd, shell=True) - return retcode == 0 - -def error (msg): - sys.stderr.write(msg + "\n") - sys.exit(1) - -def launch_viewer (code): - tmpfile = '/tmp/check-deps.tmp' - tmpimage = '/tmp/check-deps-image.png' - file = open(tmpfile, 'w') - file.write(code) - file.close() - retcode = subprocess.call("dot -Tpng %s -o %s"%(tmpfile, tmpimage), shell=True) - if retcode != 0: - raise ViewerError() - - retcode = subprocess.call("eog %s"%tmpimage, shell=True) - if retcode != 0: - raise ViewerError() - - -if __name__ == '__main__': - - # Process commnad line arguments. - parser = optparse.OptionParser() - parser.usage += " " + arg_desc + "\n" + desc - parser.add_option("-m", "--outout-mode", dest="output_mode", default="dot", metavar="MODE", - help="Specify output format mode. Supported modes are 'dot' and 'flat'.") - parser.add_option("-s", "--single", action="store_true", dest="single", default=False, - help="Print only immediate dependencies of specified modules.") - parser.add_option("-g", "--gui", action="store_true", dest="gui", default=False, - help="Display dependency graph in image viewer.") - options, args = parser.parse_args() - - if options.gui: - # Check to make sure 'dot' and 'eog' are present. - if not exec_exists('dot'): - error("'dot' not found. Make sure you have 'dot' in your path.") - if not exec_exists('eog'): - error("'eog' not found. Make sure you have 'eog' in your path.") - - # GUI mode requires dot-compatible output. - options.output_mode = 'dot' - - if options.output_mode != 'dot' and options.output_mode != 'flat': - error("Unrecognized output mode: %s"%options.output_mode) - - checker = DepsCheker() - s = '' - if options.single: - if len(args) == 0: - # single mode requires module names. - raise SingleModeError() - checker.run(args) - if options.output_mode == 'dot': - s = checker.print_dot_single(args) - else: - s = checker.print_flat_single(args) - - else: - checker.run(args) - if options.output_mode == 'dot': - s = checker.print_dot_all() - else: - s = checker.print_flat_all() - - checker.print_missing_modules() - - if options.gui: - launch_viewer(s) - else: - print (s) - diff --git a/scratch/build-analysis-tools/parse-scp2.py b/scratch/build-analysis-tools/parse-scp2.py deleted file mode 100755 index 98b1b9c49..000000000 --- a/scratch/build-analysis-tools/parse-scp2.py +++ /dev/null @@ -1,855 +0,0 @@ -#!/usr/bin/env python -######################################################################## -# -# Copyright (c) 2010 Kohei Yoshida -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, -# copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following -# conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. -# -######################################################################## - -import sys, os, os.path, optparse, subprocess - -arg_desc = "" - -desc = """ -Run this script at the root of OOo source tree.""" - -# taken from setup_native/source/packinfo/packinfo_office.txt -top_modules = [ - 'gid_Module_Optional_Gnome', - 'gid_Module_Optional_Kde', - 'gid_Module_Root', - 'gid_Module_Prg_Wrt_Bin', - 'gid_Module_Prg_Calc_Bin', - 'gid_Module_Prg_Draw_Bin', - 'gid_Module_Prg_Impress_Bin', - 'gid_Module_Prg_Base_Bin', - 'gid_Module_Prg_Math_Bin', - 'gid_Module_Optional_Binfilter', - 'gid_Module_Optional_Grfflt', - 'gid_Module_Oooimprovement', - 'gid_Module_Optional_Testtool', - 'gid_Module_Optional_Oo_English', - 'gid_Module_Optional_Xsltfiltersamples', - 'gid_Module_Optional_Javafilter', - 'gid_Module_Optional_Activexcontrol', - 'gid_Module_Optional_Onlineupdate', - 'gid_Module_Optional_Pyuno', - 'gid_Module_Optional_Pymailmerge', - 'gid_Module_Optional_Headless', - 'gid_Module_Root_Files_Images', - 'gid_Module_Root_Fonts_OOo_Hidden', - 'gid_Module_Oo_Linguistic', - 'gid_Module_Root_Files_2', - 'gid_Module_Root_Files_3', - 'gid_Module_Root_Files_4', - 'gid_Module_Root_Files_5', - 'gid_Module_Root_Files_6', - 'gid_Module_Root_Files_7', - 'gid_Module_Root_Extension_Oooimprovement', - 'gid_Module_Root_Extension_Dictionary_Af', - 'gid_Module_Root_Extension_Dictionary_Ca', - 'gid_Module_Root_Extension_Dictionary_Cs', - 'gid_Module_Root_Extension_Dictionary_Da', - 'gid_Module_Root_Extension_Dictionary_De_AT', - 'gid_Module_Root_Extension_Dictionary_De_CH', - 'gid_Module_Root_Extension_Dictionary_De_DE', - 'gid_Module_Root_Extension_Dictionary_En', - 'gid_Module_Root_Extension_Dictionary_Es', - 'gid_Module_Root_Extension_Dictionary_Et', - 'gid_Module_Root_Extension_Dictionary_Fr', - 'gid_Module_Root_Extension_Dictionary_Gl', - 'gid_Module_Root_Extension_Dictionary_He', - 'gid_Module_Root_Extension_Dictionary_Hu', - 'gid_Module_Root_Extension_Dictionary_It', - 'gid_Module_Root_Extension_Dictionary_Ku_Tr', - 'gid_Module_Root_Extension_Dictionary_Lt', - 'gid_Module_Root_Extension_Dictionary_Ne', - 'gid_Module_Root_Extension_Dictionary_Nl', - 'gid_Module_Root_Extension_Dictionary_No', - 'gid_Module_Root_Extension_Dictionary_Pl', - 'gid_Module_Root_Extension_Dictionary_Pt', - 'gid_Module_Root_Extension_Dictionary_Ro', - 'gid_Module_Root_Extension_Dictionary_Ru', - 'gid_Module_Root_Extension_Dictionary_Sk', - 'gid_Module_Root_Extension_Dictionary_Sl', - 'gid_Module_Root_Extension_Dictionary_Sr', - 'gid_Module_Root_Extension_Dictionary_Sv', - 'gid_Module_Root_Extension_Dictionary_Sw', - 'gid_Module_Root_Extension_Dictionary_Th', - 'gid_Module_Root_Extension_Dictionary_Vi', - 'gid_Module_Root_Extension_Dictionary_Zu', - 'gid_Module_Optional_OGLTrans' -] - -class ErrorBase(Exception): - - def __init__ (self, name, msg, sev): - self.value = "%s: %s"%(name, msg) - self.sev = sev # error severity, 0 = least severe - - def __str__ (self): - return repr(self.value) - -class ParseError(ErrorBase): - - def __init__ (self, msg, sev = 0): - ErrorBase.__init__(self, "ParseError", msg, sev) - -class DirError(ErrorBase): - def __init__ (self, msg): - ErrorBase.__init__(self, "DirError", msg, 0) - -class ModuleError(ErrorBase): - def __init__ (self, msg): - ErrorBase.__init__(self, "ModuleError", msg, 0) - -class LinkedNode(object): - def __init__ (self, name): - self.name = name - self.parent = None - self.children = [] - -# ---------------------------------------------------------------------------- - -def error (msg): - sys.stderr.write(msg + "\n") - -def get_attr_or_fail (name, key, attrs): - if not attrs.has_key(key): - raise ParseError("%s doesn't have %s attribute, but expected."%(name, key), 1) - return attrs[key] - -# ---------------------------------------------------------------------------- - -class Scp2Tokenizer(object): - """Tokenizer for scp files.""" - - def __init__ (self, content): - self.content = content - self.tokens = [] - - def flush_buffer (self): - if len(self.buf) > 0: - self.tokens.append(self.buf) - self.buf = '' - - def run (self): - self.tokens = [] - i = 0 - n = len(self.content) - self.buf = '' - while i < n: - c = self.content[i] - if c in '\t\n': - c = ' ' - - if c in ' ;': - self.flush_buffer() - if c == ';': - self.tokens.append(c) - elif c == '"': - # String literal. Parse until reaching the closing quote. - self.flush_buffer() - i += 1 - c = self.content[i] - while c != '"': - self.buf += c - i += 1 - c = self.content[i] - self.flush_buffer() - else: - self.buf += c - i += 1 - -# ---------------------------------------------------------------------------- - -class Scp2Parser(object): - """Parser for scp files.""" - - class Type: - File = 0 - Directory = 1 - FolderItem = 2 - - NodeTypes = [ - 'DataCarrier', # ignored - 'Directory', # ignored, referenced directly from File - 'File', # done, linked from within Module - 'Folder', # ignored - 'FolderItem', # ignored for now. windows specific? - 'Installation', # ignored. I don't know what this is for. - 'Module', # done - 'Profile', # ignored - 'ProfileItem', # ignored - 'RegistryItem', # done - 'ScpAction', # ignored - 'Shortcut', # linked to File? Treat this as a child of File for now. - 'StarRegistry', # ignored, probably for StarOffice only - 'Unixlink', # done, linked from within Module - 'WindowsCustomAction' # ignored - ] - - def __init__ (self, content, filename): - self.content = content - self.filename = filename - self.nodedata = {} - - def tokenize (self): - tokenizer = Scp2Tokenizer(self.content) - tokenizer.run() - self.tokens = tokenizer.tokens - - def next (self): - self.i += 1 - - def token (self): - return self.tokens[self.i] - - def parse (self): - if len(self.tokens) == 0: - # No tokens to parse. Bail out. - return - - self.i = 0 - self.n = len(self.tokens) - while self.i < self.n: - t = self.token() - if t in Scp2Parser.NodeTypes: - name, attrs, values = self.__parseEntity() - attrs['__node_type__'] = t # type of node - attrs['__node_location__'] = self.filename # file where the node is defined - attrs['__node_values__'] = values # list of values that are not attributes (i.e. not associated with names) - if self.nodedata.has_key(name): - raise ParseError("node named %s already exists"%name, 1) - self.nodedata[name] = attrs - else: - raise ParseError("Unknown node type: %s"%t) - - self.next() - - def append_nodes (self, nodedata, nodetree): - - for key in self.nodedata.keys(): - - if nodedata.has_key(key): - raise ParseError("node named %s already exists"%key, 1) - - # Transfer all the node attributes to the caller instance. - nodedata[key] = self.nodedata[key] - - # Now, add linkage data to the parent tree instance. - - if not nodetree.has_key(key): - # Create a new linked node instance. - nodetree[key] = LinkedNode(key) - - attrs = self.nodedata[key] - - node_type = attrs['__node_type__'] - if node_type == 'Module': - self.__link_module_node(key, attrs, nodetree) - elif node_type == 'RegistryItem': - # RegistryItem entries have ModuleID to link back to a module. - self.__link_simple(key, attrs, nodetree, 'ModuleID') - elif node_type == 'Shortcut': - self.__link_simple(key, attrs, nodetree, 'FileID') - elif node_type == 'Profile': - self.__link_simple(key, attrs, nodetree, 'ModuleID') - elif node_type == 'ProfileItem': - self.__link_simple(key, attrs, nodetree, 'ProfileID') - - - def __link_simple (self, name, attrs, nodetree, pid_attr): - parentID = get_attr_or_fail(name, pid_attr, attrs) - if not nodetree.has_key(parentID): - nodetree[parentID] = LinkedNode(parentID) - if not nodetree.has_key(name): - nodetree[name] = LinkedNode(name) - - nodetree[parentID].children.append(nodetree[name]) - if nodetree[name].parent != None: - raise ParseError("parent node instance already exists for '%s'"%name, 1) - nodetree[name].parent = nodetree[parentID] - - - def __link_files (self, name, files, nodetree): - - # file list strings are formatted like this '(file1,file2,file3,....)' - if files[0] != '(' or files[-1] != ')': - raise ParseError("file list string is not formatted correctly: %s"%files) - files = files[1:-1] - list = files.split(',') - for file in list: - if not nodetree.has_key(file): - nodetree[file] = LinkedNode(file) - nodetree[name].children.append(nodetree[file]) - - - def __link_module_node (self, name, attrs, nodetree): - - if attrs.has_key('ParentID'): - parentID = attrs['ParentID'] - - if not nodetree.has_key(parentID): - nodetree[parentID] = LinkedNode(parentID) - - nodetree[parentID].children.append(nodetree[name]) - if nodetree[name].parent != None: - raise ParseError("parent node instance already exists for '%s'"%name, 1) - nodetree[name].parent = nodetree[parentID] - - if attrs.has_key('Files'): - self.__link_files(name, attrs['Files'], nodetree) - - if attrs.has_key('Unixlinks'): - self.__link_files(name, attrs['Unixlinks'], nodetree) - - - def __parseEntity (self): - self.next() - name = self.token() - if len(name) == 0: - raise ParseError("empty name", 1) - left = True - attr_name = '' - attr_value = '' - attrs = {} - values = [] - self.next() - while self.token() != 'End': - if self.token() == '=': - if not left: - raise ParseError("multiple '='s in a single line") - - if len(attr_name) == 0: - raise ParseError("empty attribute name") - - left = False - - elif left: - if self.token() == ';': - # Not a valid attribute. Store it as a 'value'. - values.append(attr_name) - attr_name = '' - else: - attr_name += self.token() - else: - # Parse all the way up to ';' - attr_value = '' - while self.token() != ';': - attr_value += self.token() - self.next() - attrs[attr_name] = attr_value - left = True - attr_name = '' - - self.next() - - return name, attrs, values - -# ---------------------------------------------------------------------------- - -class XMLFunc: - - @staticmethod - def resolve_vars (s, vars): - """Replace all ${...}s with their respective values.""" - - ret = '' - - while True: - start = s.find('${') - if start == -1: - ret += s - break - - end = s.find('}', start+2) - if end == -1: - ret += s - break - - key = s[start+2:end] - if vars.has_key(key): - ret += s[:start] + vars[key] - s = s[end+1:] - - return ret - - @staticmethod - def to_xml_name (name): - """CamelCase to camel-case""" - s = '' - n = len(name) - for i in xrange(0, n): - c = name[i] - if 'A' <= c and c <= 'Z': - if i > 0: - s += '-' - s += c.lower() - else: - s += c - return s - - @staticmethod - def add_attr (attrs, key): - s = '' - if attrs.has_key(key): - s = " %s=\"%s\""%(XMLFunc.to_xml_name(key), attrs[key]) - return s - - @staticmethod - def add_attr_localized (attrs, key, locale): - if attrs.has_key(key): - # Try non-localized name first. - return " %s=\"%s\""%(XMLFunc.to_xml_name(key), attrs[key]) - - key_localized = key + "(%s)"%locale - if attrs.has_key(key_localized): - # Try non-localized name first. - return " %s=\"%s\" locale=\"%s\""%(XMLFunc.to_xml_name(key), attrs[key_localized], locale) - - return '' - - @staticmethod - def add_attr_vars (attrs, key, vars): - if not attrs.has_key(key): - return '' - - s = " %s=\"%s\""%(XMLFunc.to_xml_name(key), XMLFunc.resolve_vars(attrs[key], vars)) - return s - - - @staticmethod - def add_attr_array (attrs, key): - - if not attrs.has_key(key): - return '' - - raw_str = attrs[key] - if len(raw_str) == 0 or raw_str[0] != '(' or raw_str[-1] != ')': - raise ParseError("%s attribute is not formatted properly: '%s'"%(key, raw_str), 1) - - if raw_str == '()': - return '' - - val = raw_str[1:-1].lower().replace('_', '-') - s = " %s=\"%s\""%(XMLFunc.to_xml_name(key), val) - return s - - - -class Scp2Processor(object): - """Collect all .scp files in scp2 directory, and run preprocessor.""" - - tmpin = "/tmp/parse-scp2.py.cpp" - tmpout = "/tmp/parse-scp2.py.out" - - SkipList = { - 'scp2/source/ooo/ure_standalone.scp': True, - 'scp2/source/sdkoo/sdkoo.scp': True, - 'scp2/source/ooo/starregistry_ooo.scp': True - } - - def __init__ (self, cur_dir, mod_output_dir, vars): - self.cur_dir = cur_dir - self.mod_output_dir = mod_output_dir - self.vars = vars - self.scp_files = [] - self.nodedata = {} - self.nodetree = {} - self.locale = 'en-US' - - # Check file paths first. - if not os.path.isfile("%s/scp2/inc/macros.inc"%self.cur_dir): - raise ParseError("You don't appear to be at the root of OOo's source tree.") - if not os.path.isdir("%s/scp2/%s/inc"%(self.cur_dir, self.mod_output_dir)): - raise ParseError("You don't appear to be at the root of OOo's source tree.") - - def to_relative (self, fullpath): - i = fullpath.find("/scp2/") - if i < 0: - return fullpath - i += 1 # skip '/' before 'scp2' - return fullpath[i:] - - def run (self): - # Collect all .scp files under scp2. - os.path.walk(self.cur_dir + "/scp2", Scp2Processor.visit, self) - - # Process each .scp file. - for scp in self.scp_files: - relpath = self.to_relative(scp) - if Scp2Processor.SkipList.has_key(relpath): - error("skipping %s"%scp) - continue - - self.process_scp(scp) - - def process_scp (self, scp): - ret = subprocess.call("cp %s %s"%(scp, Scp2Processor.tmpin), shell=True) - if ret > 0: - raise ParseError("failed to copy scp file to a temporary location.") - - subprocess.call("gcc -E -I./scp2/inc -I./scp2/%s/inc -DUNX %s 2>/dev/null | grep -v -E \"^\#\" > %s"% - (self.mod_output_dir, Scp2Processor.tmpin, Scp2Processor.tmpout), shell=True) - - file = open(Scp2Processor.tmpout, 'r') - content = file.read() - file.close() - parser = Scp2Parser(content, self.to_relative(scp)) - parser.tokenize() - try: - parser.parse() - parser.append_nodes(self.nodedata, self.nodetree) - except ParseError as e: - # Skip mal-formed files, instead of exit with error. - error (e.value) - error ("Error parsing %s"%scp) - if e.sev > 0: - # This is a severe error. Exit right away. - sys.exit(1) - - def print_summary_flat (self): - names = self.nodedata.keys() - names.sort() - for name in names: - attrs = self.nodedata[name] - node_type = attrs['__node_type__'] - print ('-'*70) - print ("%s (%s)"%(name, node_type)) - print ("[node location: %s]"%attrs['__node_location__']) - - # Print values first. - values = attrs['__node_values__'] - for value in values: - print(" %s"%value) - - # Print all attributes. - attr_names = attrs.keys() - attr_names.sort() - for attr_name in attr_names: - if attr_name in ['__node_type__', '__node_location__', '__node_values__']: - # Skip special attributes. - continue - print (" %s = %s"%(attr_name, attrs[attr_name])) - - def print_summary_tree (self, root): - - if not self.nodetree.has_key(root): - raise ModuleError("module %s not found."%root) - - node = self.nodetree[root] - self.__print_summary_tree_node(node, 0) - - def __get_fullpath (self, fileID, locale): - """Given a file identifier, construct the absolute path for that file.""" - - nodedata = self.nodedata[fileID] - filename = None - localized = False - key_localized = "Name(%s)"%locale - if nodedata.has_key('Name'): - filename = nodedata['Name'] - elif nodedata.has_key(key_localized): - filename = nodedata[key_localized] - localized = True - else: - raise DirError("%s doesn't have a name attribute."%fileID) - - if not nodedata.has_key('Dir'): - raise DirError("file %s doesn't have Dir attribute."%fileID) - - parent_dir_name = nodedata['Dir'] - - while parent_dir_name != None: - - if parent_dir_name == 'PREDEFINED_PROGDIR': - # special directory name - filename = parent_dir_name + '/' + filename - break - - if not self.nodedata.has_key(parent_dir_name): - # directory is referenced but not defined. Skip it for now. - raise DirError("directory '%s' is referenced but not defined."%parent_dir_name) - - nodedata = self.nodedata[parent_dir_name] - if nodedata.has_key('DosName'): - filename = nodedata['DosName'] + "/" + filename - elif nodedata.has_key('DosName(en-US)'): - filename = nodedata['DosName(en-US)'] + "/" + filename - elif nodedata.has_key('HostName'): - filename = nodedata['HostName'] + "/" + filename - else: - raise DirError("directory '%s' does not have either DosName or HostName attribute."%parent_dir_name) - - if nodedata.has_key('ParentID'): - parent_dir_name = nodedata['ParentID'] - else: - parent_dir_name = None - - return filename, localized - - def __print_summary_tree_node (self, node, level): - - indent = ' '*level - - if node == None: - return - - if not self.nodedata.has_key(node.name): - # This node is referenced but is not defined. Skip it. - return - - nodedata = self.nodedata[node.name] - if not self.nodedata.has_key(node.name): - raise ParseError("there is no associated node data for '%s'"%node.name) - - node_type = nodedata['__node_type__'] - - name = '' - localized = False - if node_type in ['File', 'Unixlink', 'Shortcut', 'Profile']: - try: - name, localized = self.__get_fullpath(node.name, self.locale) - name = XMLFunc.resolve_vars(name, self.vars) - except DirError as e: - error(e.value) - return - - s = indent + "<%s id=\"%s\""%(XMLFunc.to_xml_name(node_type), node.name) - - if len(name) > 0: - s += " name=\"%s\""%name - - if node_type == 'Module': - s += XMLFunc.add_attr_array(nodedata, 'Styles') - - elif node_type == 'File': - s += XMLFunc.add_attr(nodedata, 'UnixRights') - s += XMLFunc.add_attr_array(nodedata, 'Styles') - - elif node_type == 'Profile': - s += XMLFunc.add_attr_array(nodedata, 'Styles') - - elif node_type == 'ProfileItem': - s += XMLFunc.add_attr(nodedata, 'Section') - s += XMLFunc.add_attr(nodedata, 'Key') - s += XMLFunc.add_attr_vars(nodedata, 'Value', self.vars) - - elif node_type == 'Unixlink': - s += XMLFunc.add_attr_vars(nodedata, 'Target', self.vars) - - elif node_type == 'RegistryItem': - val_path = get_attr_or_fail(node.name, 'ParentID', nodedata) - val_path += '\\' + get_attr_or_fail(node.name, 'Subkey', nodedata) - s += " path=\"%s\""%val_path - s += XMLFunc.add_attr_localized(nodedata, 'Value', self.locale) - - if localized: - s += " locale=\"%s\""%self.locale - - if len(node.children) > 0: - s += ">" - print (s) - - children = node.children - children.sort() - for child in children: - self.__print_summary_tree_node(child, level+1) - - print (indent + "</%s>"%XMLFunc.to_xml_name(node_type)) - else: - s += "/>" - print (s) - - @staticmethod - def visit (arg, dirname, names): - instance = arg - for name in names: - filepath = dirname + "/" + name - if os.path.splitext(filepath)[1] == '.scp': - instance.scp_files.append(filepath) - -# ---------------------------------------------------------------------------- - -class OOLstParser(object): - """Parser for openoffice.lst file.""" - - def __init__ (self): - self.vars = {} - - def __repr__ (self): - s = '' - scope_names = self.vars.keys() - scope_names.sort() - for scope in scope_names: - s += "%s\n"%scope - attrs = self.vars[scope] - keys = attrs.keys() - keys.sort() - for key in keys: - s += " %s"%key - if attrs[key] != None: - s += " = %s"%attrs[key] - else: - s += " =" - s += "\n" - - return s - - def get_vars (self, scopes): - vars = {} - for scope in scopes: - for key in self.vars[scope].keys(): - vars[key] = self.vars[scope][key] - return vars - - def parse_openoffice_lst (self, lines): - - class _Error(ParseError): - def __init__ (self, msg, sev=0): - ParseError.__init__(self, "(openoffice.lst) " + msg, sev) - - self.ns = [] # namespace stack - n = len(lines) - self.last = None - for i in xrange(0, n): - words = lines[i].split() - if len(words) == 0: - # empty line - continue - - if words[0] == '{': - # new scope begins - if len(words) != 1: - raise _Error("{ is followed by a token.", 1) - if self.last == None: - raise _Error("fail to find a namespace token in the previous line.", 1) - if len(self.last) != 1: - raise _Error("line contains multiple tokens when only one token is expected.", 1) - t = self.last[0] - self.ns.append(t) - - elif words[0] == '}': - # current scope ends - self.__check_last_line() - - if len(words) != 1: - raise _Error("} is followed by a token.", 1) - self.ns.pop() - - else: - # check the last line - self.__check_last_line() - - self.last = words - - def __check_last_line (self): - if self.last == None or len(self.last) == 0: - return - - if self.last[0] in '{}': - return - - key = self.last[0] - val = None - if len(self.last) > 1: - sep = ' ' - val = sep.join(self.last[1:]) - self.__insert_attr(self.ns, key, val) - - - def __insert_attr (self, ns, key, val): - ns_str = '' # aggregate namespaces, separated by '::'s. - for name in ns: - if len(ns_str) == 0: - ns_str = name - else: - ns_str += '::' + name - - if not self.vars.has_key(ns_str): - # Create this namespace entry. - self.vars[ns_str] = {} - self.vars[ns_str][key] = val - - -# ---------------------------------------------------------------------------- - -if __name__ == '__main__': - - parser = optparse.OptionParser() - parser.usage += " " + arg_desc + "\n" + desc - parser.add_option("", "--module-output-dir", dest="mod_output_dir", default="unxlngi6.pro", metavar="DIR", - help="Specify the name of module output directory. The default value is 'unxlngi6.pro'.") - parser.add_option("-m", "--output-mode", dest="mode", default='tree', metavar="MODE", - help="Specify output mode. Allowed values are 'tree' and 'flat. The default mode is 'tree'.") - parser.add_option("", "--openoffice-lst", dest="openoffice_lst", default="instsetoo_native/util/openoffice.lst", metavar="FILE", - help="Specify the location of openoffice.lst file which contains variables used by the scp files. The default value is 'instsetoo_native/util/openoffice.lst'.") - - options, args = parser.parse_args() - - if not options.mode in ['tree', 'flat']: - error("unknown output mode '%s'"%options.mode) - sys.exit(1) - - cur_dir = os.getcwd() - oo_lst_path = cur_dir + '/' + options.openoffice_lst - if not os.path.isfile(oo_lst_path): - error("failed to find the openoffice.lst file at (%s)."%oo_lst_path) - sys.exit(1) - - oolst_parser = OOLstParser() - try: - file = open(oo_lst_path, 'r') - oolst_parser.parse_openoffice_lst(file.readlines()) - file.close() - except ParseError as e: - error(e.value) - if e.sev > 0: - sys.exit(1) - - # For now, just pull variables from these two namespaces. - scopes_to_use = ['Globals::Settings::variables', 'OpenOffice::Settings::variables'] - vars = oolst_parser.get_vars(scopes_to_use) - if vars.has_key('PRODUCTNAME'): - # Special variable - vars['UNIXPRODUCTNAME'] = vars['PRODUCTNAME'].lower() - - try: - processor = Scp2Processor(cur_dir, options.mod_output_dir, vars) - processor.run() - if options.mode == 'tree': - for module in top_modules: - try: - processor.print_summary_tree(module) - except ModuleError as e: - error(e.value) - - elif options.mode == 'flat': - processor.print_summary_flat() - else: - raise ParseError("unknown output mode '%s'"%options.mode) - - except ParseError as e: - error (e.value) - sys.exit(1) |