release
[pygobject.git] / setup.py
blob5d06491451a4f20ad352520666b00a698f599854
1 #!/usr/bin/env python3
2 # Copyright 2017 Christoph Reiter <reiter.christoph@gmail.com>
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
17 # USA
19 import io
20 import os
21 import re
22 import sys
23 import errno
24 import subprocess
25 import tarfile
26 import sysconfig
27 from email import parser
29 import pkg_resources
30 from setuptools import setup, find_packages
31 from distutils.core import Extension, Distribution, Command
32 from distutils.errors import DistutilsSetupError
33 from distutils.ccompiler import new_compiler
34 from distutils.sysconfig import get_python_lib
35 from distutils import dir_util, log
38 def get_command_class(name):
39 # Returns the right class for either distutils or setuptools
40 return Distribution({}).get_command_class(name)
43 def get_pycairo_pkg_config_name():
44 return "py3cairo" if sys.version_info[0] == 3 else "pycairo"
47 def get_version_requirement(conf_dir, pkg_config_name):
48 """Given a pkg-config module name gets the minimum version required"""
50 if pkg_config_name in ["cairo", "cairo-gobject"]:
51 return "0"
53 mapping = {
54 "gobject-introspection-1.0": "introspection",
55 "glib-2.0": "glib",
56 "gio-2.0": "gio",
57 get_pycairo_pkg_config_name(): "pycairo",
58 "libffi": "libffi",
60 assert pkg_config_name in mapping
62 configure_ac = os.path.join(conf_dir, "configure.ac")
63 with io.open(configure_ac, "r", encoding="utf-8") as h:
64 text = h.read()
65 conf_name = mapping[pkg_config_name]
66 res = re.findall(
67 r"%s_required_version,\s*([\d\.]+)\)" % conf_name, text)
68 assert len(res) == 1
69 return res[0]
72 def parse_versions(conf_dir):
73 configure_ac = os.path.join(conf_dir, "configure.ac")
74 with io.open(configure_ac, "r", encoding="utf-8") as h:
75 version = re.findall(r"pygobject_[^\s]+_version,\s*(\d+)\)", h.read())
76 assert len(version) == 3
78 versions = {
79 "PYGOBJECT_MAJOR_VERSION": version[0],
80 "PYGOBJECT_MINOR_VERSION": version[1],
81 "PYGOBJECT_MICRO_VERSION": version[2],
82 "VERSION": ".".join(version),
84 return versions
87 def parse_pkg_info(conf_dir):
88 """Returns an email.message.Message instance containing the content
89 of the PKG-INFO file. The version info is parsed from configure.ac
90 """
92 versions = parse_versions(conf_dir)
94 pkg_info = os.path.join(conf_dir, "PKG-INFO.in")
95 with io.open(pkg_info, "r", encoding="utf-8") as h:
96 text = h.read()
97 for key, value in versions.items():
98 text = text.replace("@%s@" % key, value)
100 p = parser.Parser()
101 message = p.parse(io.StringIO(text))
102 return message
105 def _run_pkg_config(args):
106 command = ["pkg-config"] + args
108 try:
109 return subprocess.check_output(command)
110 except OSError as e:
111 if e.errno == errno.ENOENT:
112 raise SystemExit(
113 "%r not found.\nArguments: %r" % (command[0], command))
114 raise SystemExit(e)
115 except subprocess.CalledProcessError as e:
116 raise SystemExit(e)
119 def pkg_config_version_check(pkg, version):
120 _run_pkg_config([
121 "--print-errors",
122 "--exists",
123 '%s >= %s' % (pkg, version),
127 def pkg_config_parse(opt, pkg):
128 ret = _run_pkg_config([opt, pkg])
129 if sys.version_info[0] == 3:
130 output = ret.decode()
131 else:
132 output = ret
133 opt = opt[-2:]
134 return [x.lstrip(opt) for x in output.split()]
137 du_sdist = get_command_class("sdist")
140 class distcheck(du_sdist):
141 """Creates a tarball and does some additional sanity checks such as
142 checking if the tarball includes all files, builds successfully and
143 the tests suite passes.
146 def _check_manifest(self):
147 # make sure MANIFEST.in includes all tracked files
148 assert self.get_archive_files()
150 if subprocess.call(["git", "status"],
151 stdout=subprocess.PIPE,
152 stderr=subprocess.PIPE) != 0:
153 return
155 included_files = self.filelist.files
156 assert included_files
158 process = subprocess.Popen(
159 ["git", "ls-tree", "-r", "HEAD", "--name-only"],
160 stdout=subprocess.PIPE, universal_newlines=True)
161 out, err = process.communicate()
162 assert process.returncode == 0
164 tracked_files = out.splitlines()
165 for ignore in [".gitignore"]:
166 tracked_files.remove(ignore)
168 diff = set(tracked_files) - set(included_files)
169 assert not diff, (
170 "Not all tracked files included in tarball, check MANIFEST.in",
171 diff)
173 def _check_dist(self):
174 # make sure the tarball builds
175 assert self.get_archive_files()
177 distcheck_dir = os.path.abspath(
178 os.path.join(self.dist_dir, "distcheck"))
179 if os.path.exists(distcheck_dir):
180 dir_util.remove_tree(distcheck_dir)
181 self.mkpath(distcheck_dir)
183 archive = self.get_archive_files()[0]
184 tfile = tarfile.open(archive, "r:gz")
185 tfile.extractall(distcheck_dir)
186 tfile.close()
188 name = self.distribution.get_fullname()
189 extract_dir = os.path.join(distcheck_dir, name)
191 old_pwd = os.getcwd()
192 os.chdir(extract_dir)
193 try:
194 self.spawn([sys.executable, "setup.py", "build"])
195 self.spawn([sys.executable, "setup.py", "install",
196 "--root",
197 os.path.join(distcheck_dir, "prefix"),
198 "--record",
199 os.path.join(distcheck_dir, "log.txt"),
201 self.spawn([sys.executable, "setup.py", "test"])
202 finally:
203 os.chdir(old_pwd)
205 def run(self):
206 du_sdist.run(self)
207 self._check_manifest()
208 self._check_dist()
211 class build_tests(Command):
212 description = "build test libraries and extensions"
213 user_options = [
214 ("force", "f", "force a rebuild"),
217 def initialize_options(self):
218 self.build_temp = None
219 self.force = False
221 def finalize_options(self):
222 self.set_undefined_options(
223 'build_ext',
224 ('build_temp', 'build_temp'))
226 def _newer_group(self, sources, *targets):
227 assert targets
229 from distutils.dep_util import newer_group
231 if self.force:
232 return True
233 else:
234 for target in targets:
235 if not newer_group(sources, target):
236 return False
237 return True
239 def run(self):
240 from distutils.ccompiler import new_compiler
241 from distutils.sysconfig import customize_compiler
243 gidatadir = pkg_config_parse(
244 "--variable=gidatadir", "gobject-introspection-1.0")[0]
245 g_ir_scanner = pkg_config_parse(
246 "--variable=g_ir_scanner", "gobject-introspection-1.0")[0]
247 g_ir_compiler = pkg_config_parse(
248 "--variable=g_ir_compiler", "gobject-introspection-1.0")[0]
250 script_dir = get_script_dir()
251 tests_dir = os.path.join(script_dir, "tests")
252 gi_tests_dir = os.path.join(gidatadir, "tests")
254 schema_xml = os.path.join(tests_dir, "org.gnome.test.gschema.xml")
255 schema_bin = os.path.join(tests_dir, "gschemas.compiled")
256 if self._newer_group([schema_xml], schema_bin):
257 subprocess.check_call([
258 "glib-compile-schemas",
259 "--targetdir=%s" % tests_dir,
260 "--schema-file=%s" % schema_xml,
263 compiler = new_compiler()
264 customize_compiler(compiler)
266 if os.name == "nt":
267 compiler.shared_lib_extension = ".dll"
269 if sys.platform == "darwin":
270 compiler.shared_lib_extension = ".dylib"
271 if "-bundle" in compiler.linker_so:
272 compiler.linker_so = list(compiler.linker_so)
273 i = compiler.linker_so.index("-bundle")
274 compiler.linker_so[i] = "-dynamiclib"
276 def build_ext(ext):
277 if compiler.compiler_type == "msvc":
278 raise Exception("MSVC support not implemented")
280 libname = compiler.shared_object_filename(ext.name)
281 ext_paths = [os.path.join(tests_dir, libname)]
282 if os.name == "nt":
283 implibname = libname + ".a"
284 ext_paths.append(os.path.join(tests_dir, implibname))
286 if self._newer_group(ext.sources + ext.depends, *ext_paths):
287 objects = compiler.compile(
288 ext.sources,
289 output_dir=self.build_temp,
290 include_dirs=ext.include_dirs)
292 if os.name == "nt":
293 postargs = ["-Wl,--out-implib=%s" %
294 os.path.join(tests_dir, implibname)]
295 else:
296 postargs = []
298 compiler.link_shared_object(
299 objects,
300 compiler.shared_object_filename(ext.name),
301 output_dir=tests_dir,
302 libraries=ext.libraries,
303 library_dirs=ext.library_dirs,
304 extra_postargs=postargs)
306 return ext_paths
308 ext = Extension(
309 name='libgimarshallingtests',
310 sources=[
311 os.path.join(gi_tests_dir, "gimarshallingtests.c"),
312 os.path.join(tests_dir, "gimarshallingtestsextra.c"),
314 include_dirs=[
315 gi_tests_dir,
316 tests_dir,
318 depends=[
319 os.path.join(gi_tests_dir, "gimarshallingtests.h"),
320 os.path.join(tests_dir, "gimarshallingtestsextra.h"),
323 add_ext_pkg_config_dep(ext, compiler.compiler_type, "glib-2.0")
324 add_ext_pkg_config_dep(ext, compiler.compiler_type, "gio-2.0")
325 ext_paths = build_ext(ext)
327 gir_path = os.path.join(tests_dir, "GIMarshallingTests-1.0.gir")
328 typelib_path = os.path.join(
329 tests_dir, "GIMarshallingTests-1.0.typelib")
331 if self._newer_group(ext_paths, gir_path):
332 subprocess.check_call([
333 g_ir_scanner,
334 "--no-libtool",
335 "--include=Gio-2.0",
336 "--namespace=GIMarshallingTests",
337 "--nsversion=1.0",
338 "--symbol-prefix=gi_marshalling_tests",
339 "--warn-all",
340 "--warn-error",
341 "--library-path=%s" % tests_dir,
342 "--library=gimarshallingtests",
343 "--pkg=glib-2.0",
344 "--pkg=gio-2.0",
345 "--output=%s" % gir_path,
346 ] + ext.sources + ext.depends)
348 if self._newer_group([gir_path], typelib_path):
349 subprocess.check_call([
350 g_ir_compiler,
351 gir_path,
352 "--output=%s" % typelib_path,
355 ext = Extension(
356 name='libregress',
357 sources=[
358 os.path.join(gi_tests_dir, "regress.c"),
360 include_dirs=[
361 gi_tests_dir,
363 depends=[
364 os.path.join(gi_tests_dir, "regress.h"),
367 add_ext_pkg_config_dep(ext, compiler.compiler_type, "glib-2.0")
368 add_ext_pkg_config_dep(ext, compiler.compiler_type, "gio-2.0")
369 add_ext_pkg_config_dep(ext, compiler.compiler_type, "cairo")
370 add_ext_pkg_config_dep(ext, compiler.compiler_type, "cairo-gobject")
371 ext_paths = build_ext(ext)
373 gir_path = os.path.join(tests_dir, "Regress-1.0.gir")
374 typelib_path = os.path.join(tests_dir, "Regress-1.0.typelib")
376 if self._newer_group(ext_paths, gir_path):
377 subprocess.check_call([
378 g_ir_scanner,
379 "--no-libtool",
380 "--include=cairo-1.0",
381 "--include=Gio-2.0",
382 "--namespace=Regress",
383 "--nsversion=1.0",
384 "--warn-all",
385 "--warn-error",
386 "--library-path=%s" % tests_dir,
387 "--library=regress",
388 "--pkg=glib-2.0",
389 "--pkg=gio-2.0",
390 "--pkg=cairo",
391 "--pkg=cairo-gobject",
392 "--output=%s" % gir_path,
393 ] + ext.sources + ext.depends)
395 if self._newer_group([gir_path], typelib_path):
396 subprocess.check_call([
397 g_ir_compiler,
398 gir_path,
399 "--output=%s" % typelib_path,
402 ext = Extension(
403 name='tests.testhelper',
404 sources=[
405 os.path.join(tests_dir, "testhelpermodule.c"),
406 os.path.join(tests_dir, "test-floating.c"),
407 os.path.join(tests_dir, "test-thread.c"),
408 os.path.join(tests_dir, "test-unknown.c"),
410 include_dirs=[
411 os.path.join(script_dir, "gi"),
412 tests_dir,
414 depends=[
415 os.path.join(tests_dir, "test-thread.h"),
416 os.path.join(tests_dir, "test-unknown.h"),
417 os.path.join(tests_dir, "test-floating.h"),
420 add_ext_pkg_config_dep(ext, compiler.compiler_type, "glib-2.0")
421 add_ext_pkg_config_dep(ext, compiler.compiler_type, "gio-2.0")
422 add_ext_pkg_config_dep(ext, compiler.compiler_type, "cairo")
424 dist = Distribution({"ext_modules": [ext]})
425 cmd = dist.get_command_obj("build_ext")
426 cmd.inplace = True
427 cmd.ensure_finalized()
428 cmd.run()
431 class test(Command):
432 user_options = []
434 def initialize_options(self):
435 pass
437 def finalize_options(self):
438 pass
440 def run(self):
441 cmd = self.reinitialize_command("build_ext")
442 cmd.inplace = True
443 cmd.ensure_finalized()
444 cmd.run()
446 cmd = self.reinitialize_command("build_tests")
447 cmd.ensure_finalized()
448 cmd.run()
450 env = os.environ.copy()
451 env.pop("MSYSTEM", None)
453 pre_args = []
454 try:
455 subprocess.check_call(["dbus-run-session", "--", "true"])
456 except (EnvironmentError, subprocess.CalledProcessError):
457 # Spawning a bus failed, disable dbus instead of inheriting
458 # the user one
459 env["DBUS_SESSION_BUS_ADDRESS"] = ""
460 else:
461 pre_args = ["dbus-run-session", "--"]
463 tests_dir = os.path.join(get_script_dir(), "tests")
464 subprocess.check_call(pre_args + [
465 sys.executable,
466 os.path.join(tests_dir, "runtests.py"),
467 ], env=env)
469 if not env.get("TEST_NAMES"):
470 env["TEST_NAMES"] = "compat_test_pygtk"
471 subprocess.check_call(pre_args + [
472 sys.executable,
473 os.path.join(tests_dir, "runtests.py"),
474 ], env=env)
477 class quality(Command):
478 description = "run code quality tests"
479 user_options = []
481 def initialize_options(self):
482 pass
484 def finalize_options(self):
485 pass
487 def run(self):
488 status = subprocess.call([
489 sys.executable, "-m", "flake8",
490 ], cwd=get_script_dir())
491 if status != 0:
492 raise SystemExit(status)
495 def get_script_dir():
496 return os.path.dirname(os.path.realpath(__file__))
499 def get_pycairo_include_dir():
500 """Returns the best guess at where to find the pycairo headers.
501 A bit convoluted because we have to deal with multiple pycairo
502 versions.
504 Raises if pycairo isn't found or it's too old.
507 script_dir = get_script_dir()
508 pkg_config_name = get_pycairo_pkg_config_name()
509 min_version = get_version_requirement(script_dir, pkg_config_name)
511 def check_path(include_dir):
512 log.info("pycairo: trying include directory: %r" % include_dir)
513 header_path = os.path.join(include_dir, "%s.h" % pkg_config_name)
514 if os.path.exists(header_path):
515 log.info("pycairo: found %r" % header_path)
516 return True
517 log.info("pycairo: header file (%r) not found" % header_path)
518 return False
520 def find_path(paths):
521 for p in reversed(paths):
522 if check_path(p):
523 return p
525 def find_new_api():
526 log.info("pycairo: new API")
527 import cairo
529 pkg_version = pkg_resources.parse_version(cairo.version)
530 pkg_min_version = pkg_resources.parse_version(min_version)
531 if pkg_version < pkg_min_version:
532 raise DistutilsSetupError(
533 "pycairo >=%s required, %s found." % (
534 pkg_min_version, pkg_version))
536 if hasattr(cairo, "get_include"):
537 return [cairo.get_include()]
538 log.info("pycairo: no get_include()")
539 return []
541 def find_old_api():
542 log.info("pycairo: old API")
543 dist = pkg_resources.get_distribution("pycairo>=%s" % min_version)
544 log.info("pycairo: found %r" % dist)
546 def samefile(src, dst):
547 # Python 2 on Windows doesn't have os.path.samefile, so we have to
548 # provide a fallback
549 if hasattr(os.path, "samefile"):
550 return os.path.samefile(src, dst)
551 os.stat(src)
552 os.stat(dst)
553 return (os.path.normcase(os.path.abspath(src)) ==
554 os.path.normcase(os.path.abspath(dst)))
556 def get_sys_path(dist, name):
557 # Returns the sysconfig path for a distribution, or None
558 location = dist.location
559 for scheme in sysconfig.get_scheme_names():
560 for path_type in ["platlib", "purelib"]:
561 path = sysconfig.get_path(path_type, scheme)
562 try:
563 if samefile(path, location):
564 return sysconfig.get_path(name, scheme)
565 except EnvironmentError:
566 pass
568 data_path = get_sys_path(dist, "data") or sys.prefix
569 return [os.path.join(data_path, "include", "pycairo")]
571 def find_pkg_config():
572 log.info("pycairo: pkg-config")
573 pkg_config_version_check(pkg_config_name, min_version)
574 return pkg_config_parse("--cflags-only-I", pkg_config_name)
576 # First the new get_include() API added in >1.15.6
577 include_dir = find_path(find_new_api())
578 if include_dir is not None:
579 return include_dir
581 # Then try to find it in the data prefix based on the module path.
582 # This works with many virtualenv/userdir setups, but not all apparently,
583 # see https://gitlab.gnome.org/GNOME/pygobject/issues/150
584 include_dir = find_path(find_old_api())
585 if include_dir is not None:
586 return include_dir
588 # Finally, fall back to pkg-config
589 include_dir = find_path(find_pkg_config())
590 if include_dir is not None:
591 return include_dir
593 raise DistutilsSetupError("Could not find pycairo headers")
596 def add_ext_pkg_config_dep(ext, compiler_type, name):
597 script_dir = get_script_dir()
599 msvc_libraries = {
600 "glib-2.0": ["glib-2.0"],
601 "gio-2.0": ["gio-2.0", "gobject-2.0", "glib-2.0"],
602 "gobject-introspection-1.0":
603 ["girepository-1.0", "gobject-2.0", "glib-2.0"],
604 "cairo": ["cairo"],
605 "cairo-gobject":
606 ["cairo-gobject", "cairo", "gobject-2.0", "glib-2.0"],
607 "libffi": ["ffi"],
610 fallback_libs = msvc_libraries[name]
611 if compiler_type == "msvc":
612 # assume that INCLUDE and LIB contains the right paths
613 ext.libraries += fallback_libs
614 else:
615 min_version = get_version_requirement(script_dir, name)
616 pkg_config_version_check(name, min_version)
617 ext.include_dirs += pkg_config_parse("--cflags-only-I", name)
618 ext.library_dirs += pkg_config_parse("--libs-only-L", name)
619 ext.libraries += pkg_config_parse("--libs-only-l", name)
622 du_build_ext = get_command_class("build_ext")
625 class build_ext(du_build_ext):
627 def initialize_options(self):
628 du_build_ext.initialize_options(self)
629 self.compiler_type = None
631 def finalize_options(self):
632 du_build_ext.finalize_options(self)
633 self.compiler_type = new_compiler(compiler=self.compiler).compiler_type
635 def _write_config_h(self):
636 script_dir = get_script_dir()
637 target = os.path.join(script_dir, "config.h")
638 versions = parse_versions(script_dir)
639 with io.open(target, 'w', encoding="utf-8") as h:
640 h.write("""
641 /* Configuration header created by setup.py - do not edit */
642 #ifndef _CONFIG_H
643 #define _CONFIG_H 1
645 #define PYGOBJECT_MAJOR_VERSION %(PYGOBJECT_MAJOR_VERSION)s
646 #define PYGOBJECT_MINOR_VERSION %(PYGOBJECT_MINOR_VERSION)s
647 #define PYGOBJECT_MICRO_VERSION %(PYGOBJECT_MICRO_VERSION)s
648 #define VERSION "%(VERSION)s"
650 #endif /* _CONFIG_H */
651 """ % versions)
653 def _setup_extensions(self):
654 ext = {e.name: e for e in self.extensions}
656 def add_dependency(ext, name):
657 add_ext_pkg_config_dep(ext, self.compiler_type, name)
659 def add_pycairo(ext):
660 ext.include_dirs += [get_pycairo_include_dir()]
662 gi_ext = ext["gi._gi"]
663 add_dependency(gi_ext, "glib-2.0")
664 add_dependency(gi_ext, "gio-2.0")
665 add_dependency(gi_ext, "gobject-introspection-1.0")
666 add_dependency(gi_ext, "libffi")
668 gi_cairo_ext = ext["gi._gi_cairo"]
669 add_dependency(gi_cairo_ext, "glib-2.0")
670 add_dependency(gi_cairo_ext, "gio-2.0")
671 add_dependency(gi_cairo_ext, "gobject-introspection-1.0")
672 add_dependency(gi_cairo_ext, "libffi")
673 add_dependency(gi_cairo_ext, "cairo")
674 add_dependency(gi_cairo_ext, "cairo-gobject")
675 add_pycairo(gi_cairo_ext)
677 def run(self):
678 self._write_config_h()
679 self._setup_extensions()
680 du_build_ext.run(self)
683 class install_pkgconfig(Command):
684 description = "install .pc file"
685 user_options = []
687 def initialize_options(self):
688 self.install_base = None
689 self.install_platbase = None
690 self.install_data = None
691 self.compiler_type = None
692 self.outfiles = []
694 def finalize_options(self):
695 self.set_undefined_options(
696 'install',
697 ('install_base', 'install_base'),
698 ('install_data', 'install_data'),
699 ('install_platbase', 'install_platbase'),
702 self.set_undefined_options(
703 'build_ext',
704 ('compiler_type', 'compiler_type'),
707 def get_outputs(self):
708 return self.outfiles
710 def get_inputs(self):
711 return []
713 def run(self):
714 cmd = self.distribution.get_command_obj("bdist_wheel", create=False)
715 if cmd is not None:
716 log.warn(
717 "Python wheels and pkg-config is not compatible. "
718 "No pkg-config file will be included in the wheel. Install "
719 "from source if you need one.")
720 return
722 if self.compiler_type == "msvc":
723 return
725 script_dir = get_script_dir()
726 pkgconfig_in = os.path.join(script_dir, "pygobject-3.0.pc.in")
727 with io.open(pkgconfig_in, "r", encoding="utf-8") as h:
728 content = h.read()
730 config = {
731 "prefix": self.install_base,
732 "exec_prefix": self.install_platbase,
733 "includedir": "${prefix}/include",
734 "datarootdir": "${prefix}/share",
735 "datadir": "${datarootdir}",
736 "VERSION": self.distribution.get_version(),
738 for key, value in config.items():
739 content = content.replace("@%s@" % key, value)
741 libdir = os.path.dirname(get_python_lib(True, True, self.install_data))
742 pkgconfig_dir = os.path.join(libdir, "pkgconfig")
743 self.mkpath(pkgconfig_dir)
744 target = os.path.join(pkgconfig_dir, "pygobject-3.0.pc")
745 with io.open(target, "w", encoding="utf-8") as h:
746 h.write(content)
747 self.outfiles.append(target)
750 du_install = get_command_class("install")
753 class install(du_install):
755 sub_commands = du_install.sub_commands + [
756 ("install_pkgconfig", lambda self: True),
760 def main():
761 script_dir = get_script_dir()
762 pkginfo = parse_pkg_info(script_dir)
763 gi_dir = os.path.join(script_dir, "gi")
765 sources = [
766 os.path.join("gi", n) for n in os.listdir(gi_dir)
767 if os.path.splitext(n)[-1] == ".c"
769 cairo_sources = [os.path.join("gi", "pygi-foreign-cairo.c")]
770 for s in cairo_sources:
771 sources.remove(s)
773 readme = os.path.join(script_dir, "README.rst")
774 with io.open(readme, encoding="utf-8") as h:
775 long_description = h.read()
777 gi_ext = Extension(
778 name='gi._gi',
779 sources=sources,
780 include_dirs=[script_dir, gi_dir],
781 define_macros=[("HAVE_CONFIG_H", None)],
784 gi_cairo_ext = Extension(
785 name='gi._gi_cairo',
786 sources=cairo_sources,
787 include_dirs=[script_dir, gi_dir],
788 define_macros=[("HAVE_CONFIG_H", None)],
791 setup(
792 name=pkginfo["Name"],
793 version=pkginfo["Version"],
794 description=pkginfo["Summary"],
795 url=pkginfo["Home-page"],
796 author=pkginfo["Author"],
797 author_email=pkginfo["Author-email"],
798 maintainer=pkginfo["Maintainer"],
799 maintainer_email=pkginfo["Maintainer-email"],
800 license=pkginfo["License"],
801 long_description=long_description,
802 platforms=pkginfo.get_all("Platform"),
803 classifiers=pkginfo.get_all("Classifier"),
804 packages=find_packages(script_dir),
805 ext_modules=[
806 gi_ext,
807 gi_cairo_ext,
809 cmdclass={
810 "build_ext": build_ext,
811 "distcheck": distcheck,
812 "build_tests": build_tests,
813 "test": test,
814 "quality": quality,
815 "install": install,
816 "install_pkgconfig": install_pkgconfig,
818 install_requires=[
819 "pycairo>=%s" % get_version_requirement(
820 script_dir, get_pycairo_pkg_config_name()),
822 data_files=[
823 ('include/pygobject-3.0', ['gi/pygobject.h']),
825 zip_safe=False,
829 if __name__ == "__main__":
830 main()