#!/usr/bin/env python
'''Install P4V addins'''
# Copyright (c) 2006 Qualcomm
# Miki Tebeka <mtebeka@qualcomm.com>
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)