virt-manager/setup.py
Cole Robinson d5d6cfff2c Copy urlgrabber progress bar code into virt-manager.git
This is so we can drop the dep on system python-urlgrabber, which will
block us from going to python3.

All we need is like 300 lines from python-urlgrabber for the progress
bar. In reality our needs are much lower, we don't need the fancy
progress bar that urlgrabber provides, but it's nice to have. So if
keeping a copy of this code causes issues in the future, we can probably
come up with something simpler (or hopefully there's a more common
python progressbar impl that we can use at that point).
2015-09-18 20:55:44 -04:00

653 lines
21 KiB
Python
Executable File

#!/usr/bin/env python2
# Copyright (C) 2013, 2014 Red Hat, Inc.
import glob
import fnmatch
import os
import sys
import unittest
from distutils.core import Command, setup
from distutils.command.build import build
from distutils.command.install import install
from distutils.command.install_egg_info import install_egg_info
from distutils.command.sdist import sdist
from distutils.sysconfig import get_config_var
sysprefix = get_config_var("prefix")
from virtcli import CLIConfig
# pylint: disable=attribute-defined-outside-init
def _generate_potfiles_in():
def find(dirname, ext):
ret = []
for root, ignore, filenames in os.walk(dirname):
for filename in fnmatch.filter(filenames, ext):
ret.append(os.path.join(root, filename))
ret.sort(key=lambda s: s.lower())
return ret
scripts = ["virt-manager", "virt-install",
"virt-clone", "virt-convert", "virt-xml"]
potfiles = "\n".join(scripts) + "\n\n"
potfiles += "\n".join(find("virtManager", "*.py")) + "\n\n"
potfiles += "\n".join(find("virtcli", "*.py")) + "\n\n"
potfiles += "\n".join(find("virtconv", "*.py")) + "\n\n"
potfiles += "\n".join(find("virtinst", "*.py")) + "\n\n"
potfiles += "\n".join(["[type: gettext/glade]" + f for
f in find("ui", "*.ui")])
return potfiles
class my_build_i18n(build):
"""
Add our desktop files to the list, saves us having to track setup.cfg
"""
user_options = [
('merge-po', 'm', 'merge po files against template'),
]
def initialize_options(self):
self.merge_po = False
def finalize_options(self):
pass
def run(self):
potfiles = _generate_potfiles_in()
potpath = "po/POTFILES.in"
try:
print "Writing %s" % potpath
file(potpath, "w").write(potfiles)
self._run()
finally:
print "Removing %s" % potpath
os.unlink(potpath)
def _run(self):
# Borrowed from python-distutils-extra
desktop_files = [
("share/applications", ["data/virt-manager.desktop.in"]),
("share/appdata", ["data/virt-manager.appdata.xml"]),
]
po_dir = "po"
# Update po(t) files and print a report
# We have to change the working dir to the po dir for intltool
cmd = ["intltool-update",
(self.merge_po and "-r" or "-p"), "-g", "virt-manager"]
wd = os.getcwd()
os.chdir("po")
self.spawn(cmd)
os.chdir(wd)
max_po_mtime = 0
for po_file in glob.glob("%s/*.po" % po_dir):
lang = os.path.basename(po_file[:-3])
mo_dir = os.path.join("build", "mo", lang, "LC_MESSAGES")
mo_file = os.path.join(mo_dir, "virt-manager.mo")
if not os.path.exists(mo_dir):
os.makedirs(mo_dir)
cmd = ["msgfmt", po_file, "-o", mo_file]
po_mtime = os.path.getmtime(po_file)
mo_mtime = (os.path.exists(mo_file) and
os.path.getmtime(mo_file)) or 0
if po_mtime > max_po_mtime:
max_po_mtime = po_mtime
if po_mtime > mo_mtime:
self.spawn(cmd)
targetpath = os.path.join("share/locale", lang, "LC_MESSAGES")
self.distribution.data_files.append((targetpath, (mo_file,)))
# merge .in with translation
for (file_set, switch) in [(desktop_files, "-d")]:
for (target, files) in file_set:
build_target = os.path.join("build", target)
if not os.path.exists(build_target):
os.makedirs(build_target)
files_merged = []
for f in files:
if f.endswith(".in"):
file_merged = os.path.basename(f[:-3])
else:
file_merged = os.path.basename(f)
file_merged = os.path.join(build_target, file_merged)
cmd = ["intltool-merge", switch, po_dir, f,
file_merged]
mtime_merged = (os.path.exists(file_merged) and
os.path.getmtime(file_merged)) or 0
mtime_file = os.path.getmtime(f)
if (mtime_merged < max_po_mtime or
mtime_merged < mtime_file):
# Only build if output is older than input (.po,.in)
self.spawn(cmd)
files_merged.append(file_merged)
self.distribution.data_files.append((target, files_merged))
class my_build(build):
"""
Create simple shell wrappers for /usr/bin/ tools to point to /usr/share
Compile .pod file
"""
def _make_bin_wrappers(self):
cmds = ["virt-manager", "virt-install", "virt-clone",
"virt-convert", "virt-xml"]
if not os.path.exists("build"):
os.mkdir("build")
for app in cmds:
sharepath = os.path.join(CLIConfig.prefix,
"share", "virt-manager", app)
wrapper = "#!/bin/sh\n\n"
wrapper += "exec \"%s\" \"$@\"" % (sharepath)
newpath = os.path.abspath(os.path.join("build", app))
print "Generating %s" % newpath
file(newpath, "w").write(wrapper)
def _make_man_pages(self):
for path in glob.glob("man/*.pod"):
base = os.path.basename(path)
appname = os.path.splitext(base)[0]
newpath = os.path.join(os.path.dirname(path),
appname + ".1")
print "Generating %s" % newpath
ret = os.system('pod2man '
'--center "Virtual Machine Manager" '
'--release %s --name %s '
'< %s > %s' % (CLIConfig.version,
appname.upper(),
path, newpath))
if ret != 0:
raise RuntimeError("Generating '%s' failed." % newpath)
if os.system("grep -IRq 'Hey!' man/") == 0:
raise RuntimeError("man pages have errors in them! "
"(grep for 'Hey!')")
def _build_icons(self):
for size in glob.glob(os.path.join("data/icons", "*")):
for category in glob.glob(os.path.join(size, "*")):
icons = []
for icon in glob.glob(os.path.join(category, "*")):
icons.append(icon)
if not icons:
continue
category = os.path.basename(category)
dest = ("share/icons/hicolor/%s/%s" %
(os.path.basename(size), category))
if category != "apps":
dest = dest.replace("share/", "share/virt-manager/")
self.distribution.data_files.append((dest, icons))
def run(self):
self._make_bin_wrappers()
self._make_man_pages()
self._build_icons()
self.run_command("build_i18n")
build.run(self)
class my_egg_info(install_egg_info):
"""
Disable egg_info installation, seems pointless for a non-library
"""
def run(self):
pass
class my_install(install):
"""
Error if we weren't 'configure'd with the correct install prefix
"""
def finalize_options(self):
if self.prefix is None:
if CLIConfig.prefix != sysprefix:
print "Using prefix from 'configure': %s" % CLIConfig.prefix
self.prefix = CLIConfig.prefix
elif self.prefix != CLIConfig.prefix:
print("Install prefix=%s doesn't match configure prefix=%s\n"
"Pass matching --prefix to 'setup.py configure'" %
(self.prefix, CLIConfig.prefix))
sys.exit(1)
if self.prefix != "/usr":
print ("WARNING: GSettings may not find your schema if it's\n"
"not in /usr/share. You may need to manually play with\n"
"GSETTINGS_SCHEMA_DIR and glib-compile-schemas.")
install.finalize_options(self)
class my_sdist(sdist):
description = "Update virt-manager.spec; build sdist-tarball."
def run(self):
f1 = open('virt-manager.spec.in', 'r')
f2 = open('virt-manager.spec', 'w')
for line in f1:
f2.write(line.replace('@VERSION@', CLIConfig.version))
f1.close()
f2.close()
sdist.run(self)
###################
# Custom commands #
###################
class my_rpm(Command):
user_options = []
description = "Build src and noarch rpms."
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
"""
Run sdist, then 'rpmbuild' the tar.gz
"""
self.run_command('sdist')
os.system('rpmbuild -ta --clean dist/virt-manager-%s.tar.gz' %
CLIConfig.version)
class configure(Command):
user_options = [
("prefix=", None, "installation prefix"),
("qemu-user=", None,
"user libvirt uses to launch qemu processes (default=root)"),
("libvirt-package-names=", None,
"list of libvirt distro packages virt-manager will check for on "
"first run. comma separated string (default=none)"),
("kvm-package-names=", None,
"recommended kvm packages virt-manager will check for on first run "
"(default=none)"),
("askpass-package-names=", None,
"name of your distro's askpass package(s) (default=none)"),
("preferred-distros=", None,
"Distros to list first in the New VM wizard (default=none)"),
("stable-defaults", None,
"Hide config bits that are not considered stable (default=no)"),
("default-graphics=", None,
"Default graphics type (spice or vnc) (default=spice)"),
("default-hvs=", None,
"Comma separated list of hypervisors shown in 'Open Connection' "
"wizard. (default=all hvs)"),
]
description = "Configure the build, similar to ./configure"
def finalize_options(self):
pass
def initialize_options(self):
self.prefix = sysprefix
self.qemu_user = None
self.libvirt_package_names = None
self.kvm_package_names = None
self.askpass_package_names = None
self.preferred_distros = None
self.stable_defaults = None
self.default_graphics = None
self.default_hvs = None
def run(self):
template = ""
template += "[config]\n"
template += "prefix = %s\n" % self.prefix
if self.qemu_user is not None:
template += "default_qemu_user = %s\n" % self.qemu_user
if self.libvirt_package_names is not None:
template += "libvirt_packages = %s\n" % self.libvirt_package_names
if self.kvm_package_names is not None:
template += "hv_packages = %s\n" % self.kvm_package_names
if self.askpass_package_names is not None:
template += "askpass_packages = %s\n" % self.askpass_package_names
if self.preferred_distros is not None:
template += "preferred_distros = %s\n" % self.preferred_distros
if self.stable_defaults is not None:
template += ("stable_defaults = %s\n" %
self.stable_defaults)
if self.default_graphics is not None:
template += "default_graphics = %s\n" % self.default_graphics
if self.default_hvs is not None:
template += "default_hvs = %s\n" % self.default_hvs
file(CLIConfig.cfgpath, "w").write(template)
print "Generated %s" % CLIConfig.cfgpath
class TestBaseCommand(Command):
user_options = [
('debug', 'd', 'Show debug output'),
('coverage', 'c', 'Show coverage report'),
('regenerate-output', None, 'Regenerate test output'),
("only=", None,
"Run only testcases whose name contains the passed string"),
("testfile=", None, "Specific test file to run (e.g "
"validation, storage, ...)"),
]
def initialize_options(self):
self.debug = 0
self.regenerate_output = 0
self.coverage = 0
self.only = None
self._testfiles = []
self._dir = os.getcwd()
self.testfile = None
def finalize_options(self):
if self.debug and "DEBUG_TESTS" not in os.environ:
os.environ["DEBUG_TESTS"] = "1"
def _find_tests_in_dir(self, dirname, excludes):
testfiles = []
for t in sorted(glob.glob(os.path.join(self._dir, dirname, '*.py'))):
base = os.path.basename(t)
if base in excludes + ["__init__.py"]:
continue
if self.testfile:
check = os.path.basename(self.testfile)
if base != check and base != (check + ".py"):
continue
testfiles.append('.'.join(
dirname.split("/") + [os.path.splitext(base)[0]]))
if not testfiles:
raise RuntimeError("--testfile didn't catch anything")
return testfiles
def run(self):
try:
import coverage
use_cov = True
except:
use_cov = False
cov = None
if use_cov:
# The latter is required to not give errors on f23, probably
# a temporary bug.
omit = ["/usr/*", "/*/tests/*", "/builddir/*"]
cov = coverage.coverage(omit=omit)
cov.erase()
cov.start()
import tests as testsmodule
testsmodule.cov = cov
testsmodule.utils.REGENERATE_OUTPUT = bool(self.regenerate_output)
if hasattr(unittest, "installHandler"):
try:
unittest.installHandler()
except:
print "installHandler hack failed"
tests = unittest.TestLoader().loadTestsFromNames(self._testfiles)
if self.only:
newtests = []
for suite1 in tests:
for suite2 in suite1:
for testcase in suite2:
if self.only in str(testcase):
newtests.append(testcase)
if not newtests:
print "--only didn't find any tests"
sys.exit(1)
tests = unittest.TestSuite(newtests)
print "Running only:"
for test in newtests:
print "%s" % test
print
t = unittest.TextTestRunner(verbosity=1)
try:
result = t.run(tests)
except KeyboardInterrupt:
sys.exit(1)
if use_cov:
cov.stop()
cov.save()
err = int(bool(len(result.failures) > 0 or
len(result.errors) > 0))
if not err and use_cov and self.coverage:
cov.report(show_missing=False)
sys.exit(err)
class TestCommand(TestBaseCommand):
description = "Runs a quick unit test suite"
user_options = TestBaseCommand.user_options + [
("skipcli", None, "Skip CLI tests"),
]
def initialize_options(self):
TestBaseCommand.initialize_options(self)
self.skipcli = None
def finalize_options(self):
TestBaseCommand.finalize_options(self)
def run(self):
'''
Finds all the tests modules in tests/, and runs them.
'''
excludes = ["test_urls.py", "test_inject.py"]
if self.skipcli:
excludes += ["clitest.py"]
testfiles = self._find_tests_in_dir("tests", excludes)
# Put clitest at the end, since it takes the longest
for f in testfiles[:]:
if "clitest" in f:
testfiles.remove(f)
testfiles.append(f)
# Always want to put checkprops at the end to get accurate results
for f in testfiles[:]:
if "checkprops" in f:
testfiles.remove(f)
if not self.testfile and not self.skipcli:
testfiles.append(f)
self._testfiles = testfiles
TestBaseCommand.run(self)
class TestUI(TestBaseCommand):
description = "Run UI dogtails tests"
def run(self):
self._testfiles = self._find_tests_in_dir("tests/uitests", [])
TestBaseCommand.run(self)
class TestURLFetch(TestBaseCommand):
description = "Test fetching kernels and isos from various distro trees"
user_options = TestBaseCommand.user_options + [
("path=", None, "Paths to local iso or directory or check"
" for installable distro. Comma separated"),
]
def initialize_options(self):
TestBaseCommand.initialize_options(self)
self.path = ""
def finalize_options(self):
TestBaseCommand.finalize_options(self)
origpath = str(self.path)
if not origpath:
self.path = []
else:
self.path = origpath.split(",")
def run(self):
self._testfiles = ["tests.test_urls"]
if self.path:
import tests
tests.URLTEST_LOCAL_MEDIA += self.path
TestBaseCommand.run(self)
class TestInitrdInject(TestBaseCommand):
description = "Test initrd inject with real kernels, fetched from URLs"
user_options = TestBaseCommand.user_options + [
("distro=", None, "Comma separated list of distros to test, from "
"the tests internal URL dictionary.")
]
def initialize_options(self):
TestBaseCommand.initialize_options(self)
self.distro = ""
def finalize_options(self):
TestBaseCommand.finalize_options(self)
orig = str(self.distro)
if not orig:
self.distro = []
else:
self.distro = orig.split(",")
def run(self):
self._testfiles = ["tests.test_inject"]
if self.distro:
import tests
tests.INITRD_TEST_DISTROS += self.distro
TestBaseCommand.run(self)
class CheckPylint(Command):
user_options = []
description = "Check code using pylint and pep8"
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
files = ["setup.py", "virt-install", "virt-clone",
"virt-convert", "virt-xml", "virt-manager",
"virtcli", "virtinst", "virtconv", "virtManager",
"tests"]
output_format = sys.stdout.isatty() and "colorized" or "text"
exclude = ["virtinst/progress.py"]
print "running pep8"
cmd = "pep8 "
cmd += "--config tests/pep8.cfg "
cmd += "--exclude %s " % ",".join(exclude)
cmd += " ".join(files)
os.system(cmd)
print "running pylint"
cmd = "pylint "
cmd += "--rcfile tests/pylint.cfg "
cmd += "--output-format=%s " % output_format
cmd += "--ignore %s " % ",".join(
[os.path.basename(p) for p in exclude])
cmd += " ".join(files)
os.system(cmd)
setup(
name="virt-manager",
version=CLIConfig.version,
author="Cole Robinson",
author_email="virt-tools-list@redhat.com",
url="http://virt-manager.org",
license="GPLv2+",
# These wrappers are generated in our custom build command
scripts=([
"build/virt-manager",
"build/virt-clone",
"build/virt-install",
"build/virt-convert",
"build/virt-xml"]),
data_files=[
("share/virt-manager/", [
"virt-manager",
"virt-install",
"virt-clone",
"virt-convert",
"virt-xml",
]),
("share/glib-2.0/schemas",
["data/org.virt-manager.virt-manager.gschema.xml"]),
("share/GConf/gsettings",
["data/org.virt-manager.virt-manager.convert"]),
("share/virt-manager/ui", glob.glob("ui/*.ui")),
("share/man/man1", [
"man/virt-manager.1",
"man/virt-install.1",
"man/virt-clone.1",
"man/virt-convert.1",
"man/virt-xml.1"
]),
("share/virt-manager/virtManager", glob.glob("virtManager/*.py")),
("share/virt-manager/virtcli",
glob.glob("virtcli/*.py") + glob.glob("virtcli/cli.cfg")),
("share/virt-manager/virtinst", glob.glob("virtinst/*.py")),
("share/virt-manager/virtconv", glob.glob("virtconv/*.py")),
],
cmdclass={
'build': my_build,
'build_i18n': my_build_i18n,
'sdist': my_sdist,
'install': my_install,
'install_egg_info': my_egg_info,
'configure': configure,
'pylint': CheckPylint,
'rpm': my_rpm,
'test': TestCommand,
'test_ui': TestUI,
'test_urls' : TestURLFetch,
'test_initrd_inject' : TestInitrdInject,
}
)