#!/usr/bin/env python '''Install P4V addins''' # Copyright (c) 2006 Qualcomm # Miki Tebeka from re import match from user import home from os.path import join, isfile, dirname, isdir from sys import stdout, path, platform from urllib import quote, unquote from shutil import copy2 from os import mkdir, popen from glob import glob from elementtree.ElementTree import Element, XML, tostring from install import InstallError, load_config, is_boolean, is_process_running class Settings: '''Settings in P4V config file''' def __init__(self, name, value): self.name = name self.value = value self.children = [] def indent_level(line): '''Indent level of line (number of tabs at the beginning)''' return match("^\t*", line).end() def parse_line(line): '''Parse settings line''' fields = line.strip().split("=") if len(fields) == 1: fields.append("") return Settings(fields[0], unquote(fields[1])) # Parsing algorithm: # As long as indentation level stays the same or increases we push items to # the stack # When indentation level decreases we group all items in same indentation # level and add them to the children of the parent def parse_settings(filename): '''Parser P4V settings file. Return list of trees''' stack = [] fo = open(filename, "rt") for line in fo: indent = indent_level(line) new = parse_line(line) # 1'st item if not stack: stack.append((new, indent)) continue last_indent = stack[-1][1] if indent >= last_indent: stack.append((new, indent)) continue items = [] while indent < last_indent: if stack[-1][1] == last_indent: items.insert(0, stack.pop()[0]) continue stack[-1][0].children += items items = [] last_indent = stack[-1][1] stack.append((new, indent)) fo.close() return [s[0] for s in stack] def print_tree(tree, fo=stdout, indent=0): '''Print tree of settings''' if tree.value: print >> fo, ("\t" * indent) + ("%s=%s" % (tree.name, tree.value)) else: name = tree.name if (indent == 0) and (not name.endswith(":")): name += "=" elif name in ("directory", "prompttext"): name += "=" print >> fo, ("\t" * indent) + name for child in tree.children: print_tree(child, fo, indent + 1) def new_addin(id, args): '''Create new adding setting tree''' addin = Settings("macro%d" % id, "@") for key, value in args.items(): if is_boolean(key) and (value not in ("yes", "no")): raise InstallError("%s/%s - bad boolean value" % \ (addin["name"], key)) addin.children.append(Settings(key, quote(value))) return addin def find_node(name, nodes): '''Find a node named "name" in list of noded''' for node in nodes: if node.name == name: return node return None def find_macro(name, macros): for mac in macros: name_node = find_node("name", mac.children) assert(name_node) if name_node.value == name: return mac def config_path(name): return join(home, ".p4qt", name) def install_old(dest, addins): cfg_file = config_path("settings") settings = parse_settings(cfg_file) # Find "Macros" macros = find_node("Macros:", settings) if not macros: macros = find_node("Macros", settings) if not macros: raise InstallError("bad configuration file (no 'Macros')") if macros.name.endswith(":"): macros.name = macros.name[:-1] macros.value = "@" # Update addins inst_count = 0 for addin in addins: inst_count += 1 addin["command"] = join(dest, addin["command"]) if addin.get("directory", "") == "$install": addin["directory"] = dest # Is this just an update? mac = find_macro(addin["name"], macros.children) if not mac: mac = new_addin(len(macros.children), addin) macros.children.append(mac) else: for key in addin.keys(): find_node(key, mac.children).value = addin[key] fo = open(cfg_file, "wt") for s in settings: print_tree(s, fo) fo.close() return inst_count def yesno2truefalse(yesno): return { "yes" : "true", "no" : "false" }[yesno] def node_with_text(name, text): node = Element(name) node.text = text return node def value(addin, name): return addin[name[0].lower() + name[1:]] def new_xml_tool(addin, dest): tool = Element("CustomToolDef", {"varName" : "customtooldef"}) definition = Element("Definition") tool.append(definition) for name in ("Name", "Command", "Arguments"): definition.append(node_with_text(name, value(addin, name))) definition.append(node_with_text("InitDir", dest)) for name in ("AddToContext", "Refresh"): tool.append(node_with_text(name, yesno2truefalse(value(addin, name)))) tool.append(node_with_text("IgnoreP4Config", "true")) return tool def update_xml_tool(tool, addin, dest): definition = tool.find("Definition") for attr in ("Command", "Arguments"): node = definition.find(attr) node.text = value(addin, attr) node = definition.find("InitDir") node.text = dest for name in ("AddToContext", "Refresh"): node = tool.find(name) node.text = yesno2truefalse(value(addin, name)) node = tool.find("IgnoreP4Config") node.text = "true" def find_tool(xml, name): tool_list = xml.findall("CustomToolDef") for tool in tool_list: definition = tool.find("Definition") if definition.find("Name").text == name: return tool return None def install_new(dest, addins): cfg_file = config_path("customtools.xml") tools = XML(open(cfg_file).read()) for addin in addins: tool = find_tool(tools, addin["name"]) if not tool: tools.append(new_xml_tool(addin, dest)) else: update_xml_tool(tool, addin, dest) fo = open(cfg_file, "wt") fo.write('\n') fo.write(tostring(tools)) fo.close() def install(dest, exclude=None, check_process=0): '''Install new addins''' if check_process and is_process_running("p4v"): raise InstallError("P4V is running, close it") all_addins = load_config() if exclude: addins = [addin for addin in all_addins if addin.name not in exclude] else: addins = all_addins ok = 2 try: install_old(dest, addins) except IOError: ok -= 1 try: install_new(dest, addins) except IOError: ok -= 1 if not ok: raise InstallError if __name__ == "__main__": from optparse import OptionParser parser = OptionParser("usage: %prog [options] DESTINATION") parser.add_option("-e", help="exclude addin from install", dest="exclude", action="append", metavar="ADDIN") parser.add_option("-c", help="check that p4win is not running", dest="check", default=0, action="store_true") opts, args = parser.parse_args() if len(args) != 1: parser.error("wrong number of arguments") # Will exit dest = args[0] try: install(dest, opts.exclude, opts.check) except InstallError, e: raise SystemExit("error: %s" % e)