p4v_install.py #2

  • //
  • guest/
  • miki_tebeka/
  • p4vaddins/
  • main/
  • p4v_install.py
  • View
  • Commits
  • Open Download .zip Download (7 KB)
#!/usr/bin/env python
'''Install P4V addins'''

# Copyright (c) 2006 Qualcomm
# Miki Tebeka <[email protected]>

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('<?xml version="1.0" encoding="utf-8"?>\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)
# Change User Description Committed
#2 5628 Miki Tebeka Initial support for P4V new XML settings file
#1 5280 Miki Tebeka Initial checkin of project