setup.py 10.9 KB
Newer Older
1
#!/usr/bin/env python
2 3 4 5 6 7 8 9

from distutils.core import setup, Extension, Command
from distutils.command.build import build
from distutils.command.clean import clean
from distutils.command.sdist import sdist
from distutils.dir_util import remove_tree
from distutils.util import get_platform
from distutils.spawn import spawn
10
from distutils.errors import DistutilsExecError
11 12 13 14 15 16
import distutils

import sys
import os
import os.path
import re
17
import shutil
18 19
import time

20
MIN_LIBVIRT = "0.9.11"
21
MIN_LIBVIRT_LXC = "1.0.2"
22 23 24 25 26 27

# Hack to stop 'pip install' failing with error
# about missing 'build' dir.
if not os.path.exists("build"):
    os.mkdir("build")

28 29 30 31
_pkgcfg = -1
def get_pkgcfg(do_fail=True):
    global _pkgcfg
    if _pkgcfg == -1:
32 33
        _pkgcfg = os.getenv('PKG_CONFIG')
    if _pkgcfg is None:
34 35 36 37 38 39 40 41 42 43
        _pkgcfg = distutils.spawn.find_executable("pkg-config")
    if _pkgcfg is None and do_fail:
        raise Exception("pkg-config binary is required to compile libvirt-python")
    return _pkgcfg

def check_minimum_libvirt_version():
    spawn([get_pkgcfg(),
           "--print-errors",
           "--atleast-version=%s" % MIN_LIBVIRT,
           "libvirt"])
44

45 46
def have_libvirt_lxc():
    try:
47
        spawn([get_pkgcfg(),
48 49 50 51 52
               "--atleast-version=%s" % MIN_LIBVIRT_LXC,
             "libvirt"])
        return True
    except DistutilsExecError:
        return False
53

54 55 56 57 58 59
def have_libvirtaio():
    # This depends on asyncio, which in turn depends on "yield from" syntax.
    # The asyncio module itself is in standard library since 3.4, but there is
    # an out-of-tree version compatible with 3.3.
    return sys.version_info >= (3, 3)

60 61
def get_pkgconfig_data(args, mod, required=True):
    """Run pkg-config to and return content associated with it"""
62
    f = os.popen("%s %s %s" % (get_pkgcfg(), " ".join(args), mod))
63 64 65 66 67 68 69 70 71 72 73 74 75

    line = f.readline()
    if line is not None:
        line = line.strip()

    if line is None or line == "":
        if required:
            raise Exception("Cannot determine '%s' from libvirt pkg-config file" % " ".join(args))
        else:
            return ""

    return line

76 77 78 79 80 81 82 83 84 85 86 87 88 89
def get_api_xml_files():
    """Check with pkg-config that libvirt is present and extract
    the API XML file paths we need from it"""

    libvirt_api = get_pkgconfig_data(["--variable", "libvirt_api"], "libvirt")

    offset = libvirt_api.index("-api.xml")
    libvirt_qemu_api = libvirt_api[0:offset] + "-qemu-api.xml"

    offset = libvirt_api.index("-api.xml")
    libvirt_lxc_api = libvirt_api[0:offset] + "-lxc-api.xml"

    return (libvirt_api, libvirt_qemu_api, libvirt_lxc_api)

90 91 92 93 94
def get_module_lists():
    """
    Determine which modules we are actually building, and all their
    required config
    """
95 96 97
    if get_pkgcfg(do_fail=False) is None:
        return [], []

98 99
    c_modules = []
    py_modules = []
100 101
    ldflags = get_pkgconfig_data(["--libs-only-L"], "libvirt", False).split()
    cflags = get_pkgconfig_data(["--cflags"], "libvirt", False).split()
102 103 104 105

    module = Extension('libvirtmod',
                       sources = ['libvirt-override.c', 'build/libvirt.c', 'typewrappers.c', 'libvirt-utils.c'],
                       libraries = [ "virt" ],
106
                       include_dirs = [ "." ])
107 108
    module.extra_compile_args.extend(cflags)
    module.extra_link_args.extend(ldflags)
109

110 111
    c_modules.append(module)
    py_modules.append("libvirt")
112

113 114 115 116
    moduleqemu = Extension('libvirtmod_qemu',
                           sources = ['libvirt-qemu-override.c', 'build/libvirt-qemu.c', 'typewrappers.c', 'libvirt-utils.c'],
                           libraries = [ "virt-qemu" ],
                           include_dirs = [ "." ])
117 118
    moduleqemu.extra_compile_args.extend(cflags)
    moduleqemu.extra_link_args.extend(ldflags)
119 120 121 122

    c_modules.append(moduleqemu)
    py_modules.append("libvirt_qemu")

123
    if have_libvirt_lxc():
124 125 126 127
        modulelxc = Extension('libvirtmod_lxc',
                              sources = ['libvirt-lxc-override.c', 'build/libvirt-lxc.c', 'typewrappers.c', 'libvirt-utils.c'],
                              libraries = [ "virt-lxc" ],
                              include_dirs = [ "." ])
128 129
        modulelxc.extra_compile_args.extend(cflags)
        modulelxc.extra_link_args.extend(ldflags)
130 131 132 133

        c_modules.append(modulelxc)
        py_modules.append("libvirt_lxc")

134 135 136
    if have_libvirtaio():
        py_modules.append("libvirtaio")

137 138 139 140 141 142
    return c_modules, py_modules


###################
# Custom commands #
###################
143

144 145 146
class my_build(build):

    def run(self):
147
        check_minimum_libvirt_version()
148
        apis = get_api_xml_files()
149

150 151
        self.spawn([sys.executable, "generator.py", "libvirt", apis[0]])
        self.spawn([sys.executable, "generator.py", "libvirt-qemu", apis[1]])
152
        if have_libvirt_lxc():
153
            self.spawn([sys.executable, "generator.py", "libvirt-lxc", apis[2]])
154 155
        if have_libvirtaio():
            shutil.copy('libvirtaio.py', 'build')
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

        build.run(self)

class my_sdist(sdist):
    user_options = sdist.user_options

    description = "Update libvirt-python.spec; build sdist-tarball."

    def initialize_options(self):
        self.snapshot = None
        sdist.initialize_options(self)

    def finalize_options(self):
        if self.snapshot is not None:
            self.snapshot = 1
        sdist.finalize_options(self)

    def gen_rpm_spec(self):
        f1 = open('libvirt-python.spec.in', 'r')
        f2 = open('libvirt-python.spec', 'w')
        for line in f1:
            f2.write(line
178
                     .replace('@PY_VERSION@', self.distribution.get_version()))
179 180 181 182 183 184 185
        f1.close()
        f2.close()

    def gen_authors(self):
        f = os.popen("git log --pretty=format:'%aN <%aE>'")
        authors = []
        for line in f:
186 187 188
            line = "   " + line.strip()
            if line not in authors:
                authors.append(line)
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

        authors.sort(key=str.lower)

        f1 = open('AUTHORS.in', 'r')
        f2 = open('AUTHORS', 'w')
        for line in f1:
            f2.write(line.replace('@AUTHORS@', "\n".join(authors)))
        f1.close()
        f2.close()


    def gen_changelog(self):
        f1 = os.popen("git log '--pretty=format:%H:%ct %an  <%ae>%n%n%s%n%b%n'")
        f2 = open("ChangeLog", 'w')

        for line in f1:
            m = re.match(r'([a-f0-9]+):(\d+)\s(.*)', line)
            if m:
                t = time.gmtime(int(m.group(2)))
                f2.write("%04d-%02d-%02d %s\n" % (t.tm_year, t.tm_mon, t.tm_mday, m.group(3)))
            else:
                if re.match(r'Signed-off-by', line):
                    continue
                f2.write("    " + line.strip() + "\n")

        f1.close()
        f2.close()


    def run(self):
        if not os.path.exists("build"):
            os.mkdir("build")

        if os.path.exists(".git"):
            try:
                self.gen_rpm_spec()
                self.gen_authors()
                self.gen_changelog()

                sdist.run(self)

            finally:
                files = ["libvirt-python.spec",
                         "AUTHORS",
                         "ChangeLog"]
                for f in files:
                    if os.path.exists(f):
                        os.unlink(f)
        else:
            sdist.run(self)

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')
257 258
        self.spawn(["/usr/bin/rpmbuild", "-ta", "--clean",
            "dist/libvirt-python-%s.tar.gz" % self.distribution.get_version()])
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290

class my_test(Command):
    user_options = [
        ('build-base=', 'b',
         "base directory for build library"),
        ('build-platlib=', None,
         "build directory for platform-specific distributions"),
        ('plat-name=', 'p',
         "platform name to build for, if supported "
         "(default: %s)" % get_platform()),
    ]

    description = "Run test suite."

    def initialize_options(self):
        self.build_base = 'build'
        self.build_platlib = None
        self.plat_name = None

    def finalize_options(self):
        if self.plat_name is None:
            self.plat_name = get_platform()

        plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3])

        if hasattr(sys, 'gettotalrefcount'):
            plat_specifier += '-pydebug'

        if self.build_platlib is None:
            self.build_platlib = os.path.join(self.build_base,
                                              'lib' + plat_specifier)

291
    def find_nosetests_path(self):
292 293 294 295 296
        binaries = [
            "nosetests-%d.%d" % (sys.version_info[0],
                                 sys.version_info[1]),
            "nosetests-%d" % (sys.version_info[0]),
            "nosetests",
297 298
        ]

299 300 301
        for binary in binaries:
            path = distutils.spawn.find_executable(binary)
            if path is not None:
302 303 304 305
                return path

        raise Exception("Cannot find any nosetests binary")

306 307 308 309 310
    def run(self):
        """
        Run test suite
        """

311 312
        apis = get_api_xml_files()

313 314 315 316
        if "PYTHONPATH" in os.environ:
            os.environ["PYTHONPATH"] = self.build_platlib + ":" + os.environ["PYTHONPATH"]
        else:
            os.environ["PYTHONPATH"] = self.build_platlib
317
        self.spawn([sys.executable, "sanitytest.py", self.build_platlib, apis[0]])
318 319
        nose = self.find_nosetests_path()
        self.spawn([sys.executable, nose])
320 321 322 323 324 325 326 327 328


class my_clean(clean):
    def run(self):
        clean.run(self)

        if os.path.exists("build"):
            remove_tree("build")

329 330 331 332 333 334 335

##################
# Invoke setup() #
##################

_c_modules, _py_modules = get_module_lists()

336
setup(name = 'libvirt-python',
337
      version = '5.3.0',
338 339 340
      url = 'http://www.libvirt.org',
      maintainer = 'Libvirt Maintainers',
      maintainer_email = 'libvir-list@redhat.com',
341 342 343 344 345 346 347
      description = 'The libvirt virtualization API python binding',
      long_description =
        '''The libvirt-python package provides a module that permits applications
written in the Python programming language to call the interface
supplied by the libvirt library, to manage the virtualization capabilities
of recent versions of Linux (and other OSes).''',
      license = 'LGPLv2+',
348 349
      ext_modules = _c_modules,
      py_modules = _py_modules,
350 351 352 353 354 355 356 357 358
      package_dir = {
          '': 'build'
      },
      cmdclass = {
          'build': my_build,
          'clean': my_clean,
          'sdist': my_sdist,
          'rpm': my_rpm,
          'test': my_test
359 360 361 362 363 364 365 366 367 368
      },
      classifiers = [
          "Development Status :: 5 - Production/Stable",
          "Intended Audience :: Developers",
          "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
          "Programming Language :: Python",
          "Programming Language :: Python :: 2",
          "Programming Language :: Python :: 3",
      ]
)