Mini Shell
#
# Copyright (c) 2014, Red Hat, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of Red Hat nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Stanislav Ochotnicky <sochotnicky@redhat.com>
import codecs
import errno
import os
import re
import six
from six import BytesIO
from javapackages.maven.artifact import ArtifactValidationException
from javapackages.common.exception import JavaPackagesToolsException
from lxml.etree import ElementTree, Element, SubElement
import lxml
import lxml.etree as ET
class XMvnConfigException(JavaPackagesToolsException):
pass
class XMvnConfig(object):
"""
Class for modifying XMvn configuration
"""
INDEX_PATH = os.path.join(".xmvn", "javapackages-rule-index")
CONFIG_DIR = os.path.join(".xmvn", "config.d")
XMLNS = "http://fedorahosted.org/xmvn/CONFIG/2.0.0"
def __init__(self):
self.templateXML = u"""<?xml version='1.0' encoding='utf-8'?>
<configuration xmlns="http://fedorahosted.org/xmvn/CONFIG/2.0.0">
{content}
</configuration>
"""
try:
os.makedirs(XMvnConfig.CONFIG_DIR, 0o755)
except OSError:
# If directory already exists then it's OK. If it doesn't
# then creating the index file below will fail anyways.
pass
try:
with open(XMvnConfig.INDEX_PATH) as index:
self.index = int(index.read()) + 1
except IOError as e:
if e.errno == errno.ENOENT:
self.index = 1
else:
raise e
def __write_index(self):
with open(XMvnConfig.INDEX_PATH, 'w') as index:
index.write(str(self.index))
self.index = self.index + 1
def __get_current_config(self):
fname = 'javapackages-config-{index:05d}.xml'.format(index=self.index)
return os.path.join(XMvnConfig.CONFIG_DIR,
fname)
def __init_xml(self, content=""):
self.__write_index()
xmlbuf = BytesIO()
xmlbuf.write(self.templateXML.format(content=content).encode("UTF-8"))
root = ET.fromstring(xmlbuf.getvalue())
root.append(ET.Comment("XMvn configuration file generated by "
"javapackages.xmvn_config (part of "
"javapackages-tools)"))
return root
def __prettify_element(self, elem):
xmlbuf = BytesIO()
et = ElementTree()
et._setroot(elem)
et.write(xmlbuf,
xml_declaration=True,
encoding='utf-8',
method="xml",
pretty_print=True)
return xmlbuf.getvalue()
def __write_xml(self, path, root):
xmlstr = self.__prettify_element(root)
with codecs.open(path, 'w+', "utf-8") as fout:
fout.write(xmlstr.decode("UTF-8"))
def __add_config(self, level1, level2, level3=None, content=None):
if not content:
raise XMvnConfigException("Provide content as keyword argument")
confpath = self.__get_current_config()
root = self.__init_xml()
level1 = SubElement(root, level1)
level2 = SubElement(level1, level2)
cont_level = level2
if level3:
cont_level = SubElement(level2, level3)
if isinstance(content, six.string_types):
cont_level.text = content
elif isinstance(content, list):
for elem in content:
cont_level.append(elem)
else:
cont_level.append(content)
self.__write_xml(confpath, root)
def __count_backreferences(self, s):
"""
Return maximum number of backreference used in string s
"""
backref_re = re.compile(r'@(\d+)')
backref_nos = [int(x) for x in backref_re.findall(s)]
if backref_nos:
return max((int(x) for x in backref_re.findall(s)))
return 0
def __count_wildcard_groups(self, s):
"""
Return number of wildcard groups used in string s
"""
left = s.count('{')
right = s.count('}')
if left != right:
raise XMvnConfigException("Number of opening and closing "
"parenthesis for groups of wildcard "
"matching is different.")
return left
def add_aliases(self, artifact, aliases):
"""
Adds alias artifacts for given main artifact
artifact -- main Artifact for which aliases are being provided
aliases -- list of alternate Artifact representations
"""
artifact.validate(allow_backref=False)
wild_groups = self.__count_wildcard_groups(artifact.get_rpm_str())
main = artifact.get_xml_element(root_element_name="artifactGlob")
elems = [main]
aelem = Element("aliases")
for alias in aliases:
alias.validate(allow_empty=False, allow_wildcards=False)
backrefs = self.__count_backreferences(alias.get_rpm_str())
if backrefs > wild_groups:
raise ArtifactValidationException("Number of backrefenreces "
"is higher than wildcard "
"groups.")
aelem.append(alias.get_xml_element(root_element_name="alias"))
elems.append(aelem)
self.__add_config("artifactManagement", "rule", content=elems)
def add_compat_versions(self, artifact, versions):
"""
Change where on filesystem given artifact is installed
artifact -- Artifact to be modified
versions -- list of compat versions for given artifact
"""
artifact.validate(allow_backref=False)
wild_groups = self.__count_wildcard_groups(artifact.get_rpm_str(artifact.version))
main = artifact.get_xml_element(root_element_name="artifactGlob")
elems = [main]
velem = Element("versions")
for version in versions:
backrefs = self.__count_backreferences(version)
if backrefs > wild_groups:
raise ArtifactValidationException("Number of backrefenreces "
"is higher than wildcard "
"groups.")
ve = SubElement(velem, "version")
ve.text = version
elems.append(velem)
self.__add_config("artifactManagement", "rule", content=elems)
def add_file_mapping(self, artifact, paths):
"""
Change where on filesystem given artifact is installed
artifact -- Artifact to be modified
paths -- list of paths for given artifact
"""
artifact.validate(allow_backref=False)
wild_groups = self.__count_wildcard_groups(artifact.get_rpm_str())
main = artifact.get_xml_element(root_element_name="artifactGlob")
elems = [main]
felem = Element("files")
if not [path for path in paths if not os.path.isabs(path)]:
raise XMvnConfigException("At least one path must be relative")
for path in paths:
backrefs = self.__count_backreferences(path)
if backrefs > wild_groups:
raise ArtifactValidationException("Number of backrefenreces "
"is higher than wildcard "
"groups.")
pe = SubElement(felem, "file")
pe.text = path
elems.append(felem)
self.__add_config("artifactManagement", "rule", content=elems)
def add_package_mapping(self, artifact, package, optional=False):
"""
Change which package given artifact belongs to
artifact -- Artifact to be modified
package -- subpackage name where artifact belongs
"""
artifact.validate(allow_backref=False)
wild_groups = self.__count_wildcard_groups(artifact.get_rpm_str())
main = artifact.get_xml_element(root_element_name="artifactGlob")
backrefs = self.__count_backreferences(package)
if backrefs > wild_groups:
raise ArtifactValidationException("Number of backrefenreces "
"is higher than wildcard "
"groups.")
elems = [main]
if optional:
opt = Element("optional")
opt.text = "true"
elems.append(opt)
target = Element("targetPackage")
target.text = package
elems.append(target)
self.__add_config("artifactManagement", "rule", content=elems)
def add_custom_option(self, optionstr, content):
"""
Add custom configuration option
optionstr -- XPath-like expression for specifying XMvn configuration
option location with '/' used as delimiter
example: buildSettings/compilerSource
content -- XML content to be added to specified node. Can be just text, XML node or multiple nodes.
examples:
someText
<someNode>someText</someNode><someOtherNode/>
"""
node_names = optionstr.split("/")
confpath = self.__get_current_config()
root = self.__init_xml()
par = root
for node in node_names:
par = SubElement(par, node)
try:
# wrap content into something to allow text content
inserted = "<root>{0}</root>".format(content)
contentRoot = ET.fromstring(inserted)
par.text = contentRoot.text
for element in contentRoot:
par.append(element)
except lxml.etree.XMLSyntaxError:
raise XMvnConfigException("content is not valid content for XML node")
self.__write_xml(confpath, root)
Zerion Mini Shell 1.0