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
27 from email
import parser
30 from setuptools
import setup
31 from distutils
.core
import Extension
, Distribution
, Command
32 from distutils
.errors
import DistutilsSetupError
, DistutilsOptionError
33 from distutils
.ccompiler
import new_compiler
34 from distutils
.sysconfig
import get_python_lib
, customize_compiler
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"]:
54 "gobject-introspection-1.0": "introspection",
57 get_pycairo_pkg_config_name(): "pycairo",
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
:
65 conf_name
= mapping
[pkg_config_name
]
67 r
"%s_required_version,\s*([\d\.]+)\)" % conf_name
, text
)
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
79 "PYGOBJECT_MAJOR_VERSION": version
[0],
80 "PYGOBJECT_MINOR_VERSION": version
[1],
81 "PYGOBJECT_MICRO_VERSION": version
[2],
82 "VERSION": ".".join(version
),
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
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
:
97 for key
, value
in versions
.items():
98 text
= text
.replace("@%s@" % key
, value
)
101 message
= p
.parse(io
.StringIO(text
))
105 def _run_pkg_config(args
, _cache
={}):
106 command
= tuple(["pkg-config"] + args
)
108 if command
not in _cache
:
110 result
= subprocess
.check_output(command
)
112 if e
.errno
== errno
.ENOENT
:
114 "%r not found.\nArguments: %r" % (command
[0], command
))
116 except subprocess
.CalledProcessError
as e
:
119 _cache
[command
] = result
121 return _cache
[command
]
124 def pkg_config_version_check(pkg
, version
):
128 '%s >= %s' % (pkg
, version
),
132 def pkg_config_parse(opt
, pkg
):
133 ret
= _run_pkg_config([opt
, pkg
])
134 if sys
.version_info
[0] == 3:
135 output
= ret
.decode()
139 return [x
.lstrip(opt
) for x
in output
.split()]
142 def filter_compiler_arguments(compiler
, args
):
143 """Given a compiler instance and a list of compiler warning flags
144 returns the list of supported flags.
147 if compiler
.compiler_type
== "msvc":
153 def check_arguments(compiler
, args
):
154 p
= subprocess
.Popen(
155 [compiler
.compiler
[0]] + args
+ extra
+ ["-x", "c", "-E", "-"],
156 stdin
=subprocess
.PIPE
,
157 stdout
=subprocess
.PIPE
,
158 stderr
=subprocess
.PIPE
)
159 stdout
, stderr
= p
.communicate(b
"int i;\n")
160 if p
.returncode
!= 0:
161 text
= stderr
.decode("ascii", "replace")
162 return False, [a
for a
in args
if a
in text
]
166 def check_argument(compiler
, arg
):
167 return check_arguments(compiler
, [arg
])[0]
169 # clang doesn't error out for unknown options, force it to
170 if check_argument(compiler
, '-Werror=unknown-warning-option'):
171 extra
+= ['-Werror=unknown-warning-option']
172 if check_argument(compiler
, '-Werror=unused-command-line-argument'):
173 extra
+= ['-Werror=unused-command-line-argument']
175 # first try to remove all arguments contained in the error message
176 supported
= list(args
)
178 ok
, maybe_unknown
= check_arguments(compiler
, supported
)
181 elif not maybe_unknown
:
183 for unknown
in maybe_unknown
:
184 if not check_argument(compiler
, unknown
):
185 supported
.remove(unknown
)
187 # hm, didn't work, try each argument one by one
190 if check_argument(compiler
, arg
):
191 supported
.append(arg
)
195 du_sdist
= get_command_class("sdist")
198 class distcheck(du_sdist
):
199 """Creates a tarball and does some additional sanity checks such as
200 checking if the tarball includes all files, builds successfully and
201 the tests suite passes.
204 def _check_manifest(self
):
205 # make sure MANIFEST.in includes all tracked files
206 assert self
.get_archive_files()
208 if subprocess
.call(["git", "status"],
209 stdout
=subprocess
.PIPE
,
210 stderr
=subprocess
.PIPE
) != 0:
213 included_files
= self
.filelist
.files
214 assert included_files
216 process
= subprocess
.Popen(
217 ["git", "ls-tree", "-r", "HEAD", "--name-only"],
218 stdout
=subprocess
.PIPE
, universal_newlines
=True)
219 out
, err
= process
.communicate()
220 assert process
.returncode
== 0
222 tracked_files
= out
.splitlines()
223 for ignore
in [".gitignore"]:
224 tracked_files
.remove(ignore
)
226 diff
= set(tracked_files
) - set(included_files
)
228 "Not all tracked files included in tarball, check MANIFEST.in",
231 def _check_dist(self
):
232 # make sure the tarball builds
233 assert self
.get_archive_files()
235 distcheck_dir
= os
.path
.abspath(
236 os
.path
.join(self
.dist_dir
, "distcheck"))
237 if os
.path
.exists(distcheck_dir
):
238 dir_util
.remove_tree(distcheck_dir
)
239 self
.mkpath(distcheck_dir
)
241 archive
= self
.get_archive_files()[0]
242 tfile
= tarfile
.open(archive
, "r:gz")
243 tfile
.extractall(distcheck_dir
)
246 name
= self
.distribution
.get_fullname()
247 extract_dir
= os
.path
.join(distcheck_dir
, name
)
249 old_pwd
= os
.getcwd()
250 os
.chdir(extract_dir
)
252 self
.spawn([sys
.executable
, "setup.py", "build"])
253 self
.spawn([sys
.executable
, "setup.py", "install",
255 os
.path
.join(distcheck_dir
, "prefix"),
257 os
.path
.join(distcheck_dir
, "log.txt"),
259 self
.spawn([sys
.executable
, "setup.py", "test"])
265 self
._check
_manifest
()
269 class build_tests(Command
):
270 description
= "build test libraries and extensions"
272 ("force", "f", "force a rebuild"),
275 def initialize_options(self
):
276 self
.build_temp
= None
277 self
.build_base
= None
280 def finalize_options(self
):
281 self
.set_undefined_options(
283 ('build_temp', 'build_temp'))
284 self
.set_undefined_options(
286 ('build_base', 'build_base'))
288 def _newer_group(self
, sources
, *targets
):
291 from distutils
.dep_util
import newer_group
296 for target
in targets
:
297 if not newer_group(sources
, target
):
302 cmd
= self
.reinitialize_command("build_ext")
304 cmd
.ensure_finalized()
307 gidatadir
= pkg_config_parse(
308 "--variable=gidatadir", "gobject-introspection-1.0")[0]
309 g_ir_scanner
= pkg_config_parse(
310 "--variable=g_ir_scanner", "gobject-introspection-1.0")[0]
311 g_ir_compiler
= pkg_config_parse(
312 "--variable=g_ir_compiler", "gobject-introspection-1.0")[0]
314 script_dir
= get_script_dir()
315 tests_dir
= os
.path
.join(script_dir
, "tests")
316 gi_tests_dir
= os
.path
.join(gidatadir
, "tests")
318 schema_xml
= os
.path
.join(tests_dir
, "org.gnome.test.gschema.xml")
319 schema_bin
= os
.path
.join(tests_dir
, "gschemas.compiled")
320 if self
._newer
_group
([schema_xml
], schema_bin
):
321 subprocess
.check_call([
322 "glib-compile-schemas",
323 "--targetdir=%s" % tests_dir
,
324 "--schema-file=%s" % schema_xml
,
327 compiler
= new_compiler()
328 customize_compiler(compiler
)
331 compiler
.shared_lib_extension
= ".dll"
333 if sys
.platform
== "darwin":
334 compiler
.shared_lib_extension
= ".dylib"
335 if "-bundle" in compiler
.linker_so
:
336 compiler
.linker_so
= list(compiler
.linker_so
)
337 i
= compiler
.linker_so
.index("-bundle")
338 compiler
.linker_so
[i
] = "-dynamiclib"
341 if compiler
.compiler_type
== "msvc":
342 raise Exception("MSVC support not implemented")
344 libname
= compiler
.shared_object_filename(ext
.name
)
345 ext_paths
= [os
.path
.join(tests_dir
, libname
)]
347 implibname
= libname
+ ".a"
348 ext_paths
.append(os
.path
.join(tests_dir
, implibname
))
350 if self
._newer
_group
(ext
.sources
+ ext
.depends
, *ext_paths
):
351 objects
= compiler
.compile(
353 output_dir
=self
.build_temp
,
354 include_dirs
=ext
.include_dirs
)
357 postargs
= ["-Wl,--out-implib=%s" %
358 os
.path
.join(tests_dir
, implibname
)]
362 compiler
.link_shared_object(
364 compiler
.shared_object_filename(ext
.name
),
365 output_dir
=tests_dir
,
366 libraries
=ext
.libraries
,
367 library_dirs
=ext
.library_dirs
,
368 extra_postargs
=postargs
)
373 name
='libgimarshallingtests',
375 os
.path
.join(gi_tests_dir
, "gimarshallingtests.c"),
376 os
.path
.join(tests_dir
, "gimarshallingtestsextra.c"),
383 os
.path
.join(gi_tests_dir
, "gimarshallingtests.h"),
384 os
.path
.join(tests_dir
, "gimarshallingtestsextra.h"),
387 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "glib-2.0")
388 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "gio-2.0")
389 ext_paths
= build_ext(ext
)
391 gir_path
= os
.path
.join(tests_dir
, "GIMarshallingTests-1.0.gir")
392 typelib_path
= os
.path
.join(
393 tests_dir
, "GIMarshallingTests-1.0.typelib")
395 if self
._newer
_group
(ext_paths
, gir_path
):
396 subprocess
.check_call([
400 "--namespace=GIMarshallingTests",
402 "--symbol-prefix=gi_marshalling_tests",
405 "--library-path=%s" % tests_dir
,
406 "--library=gimarshallingtests",
409 "--output=%s" % gir_path
,
410 ] + ext
.sources
+ ext
.depends
)
412 if self
._newer
_group
([gir_path
], typelib_path
):
413 subprocess
.check_call([
416 "--output=%s" % typelib_path
,
422 os
.path
.join(gi_tests_dir
, "regress.c"),
423 os
.path
.join(tests_dir
, "regressextra.c"),
429 os
.path
.join(gi_tests_dir
, "regress.h"),
430 os
.path
.join(tests_dir
, "regressextra.h"),
433 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "glib-2.0")
434 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "gio-2.0")
435 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "cairo")
436 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "cairo-gobject")
437 ext_paths
= build_ext(ext
)
439 gir_path
= os
.path
.join(tests_dir
, "Regress-1.0.gir")
440 typelib_path
= os
.path
.join(tests_dir
, "Regress-1.0.typelib")
442 if self
._newer
_group
(ext_paths
, gir_path
):
443 subprocess
.check_call([
446 "--include=cairo-1.0",
448 "--namespace=Regress",
452 "--library-path=%s" % tests_dir
,
457 "--pkg=cairo-gobject",
458 "--output=%s" % gir_path
,
459 ] + ext
.sources
+ ext
.depends
)
461 if self
._newer
_group
([gir_path
], typelib_path
):
462 subprocess
.check_call([
465 "--output=%s" % typelib_path
,
469 name
='tests.testhelper',
471 os
.path
.join(tests_dir
, "testhelpermodule.c"),
472 os
.path
.join(tests_dir
, "test-floating.c"),
473 os
.path
.join(tests_dir
, "test-thread.c"),
474 os
.path
.join(tests_dir
, "test-unknown.c"),
477 os
.path
.join(script_dir
, "gi"),
481 os
.path
.join(tests_dir
, "test-thread.h"),
482 os
.path
.join(tests_dir
, "test-unknown.h"),
483 os
.path
.join(tests_dir
, "test-floating.h"),
486 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "glib-2.0")
487 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "gio-2.0")
488 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, "cairo")
489 add_ext_warn_flags(ext
, compiler
)
491 dist
= Distribution({"ext_modules": [ext
]})
493 build_cmd
= dist
.get_command_obj("build")
494 build_cmd
.build_base
= os
.path
.join(self
.build_base
, "pygobject_tests")
495 build_cmd
.ensure_finalized()
497 cmd
= dist
.get_command_obj("build_ext")
499 cmd
.force
= self
.force
500 cmd
.ensure_finalized()
506 ("valgrind", None, "run tests under valgrind"),
507 ("valgrind-log-file=", None, "save logs instead of printing them"),
508 ("gdb", None, "run tests under gdb"),
511 def initialize_options(self
):
513 self
.valgrind_log_file
= None
516 def finalize_options(self
):
517 self
.valgrind
= bool(self
.valgrind
)
518 if self
.valgrind_log_file
and not self
.valgrind
:
519 raise DistutilsOptionError("valgrind not enabled")
520 self
.gdb
= bool(self
.gdb
)
523 cmd
= self
.reinitialize_command("build_tests")
524 cmd
.ensure_finalized()
527 env
= os
.environ
.copy()
528 env
.pop("MSYSTEM", None)
530 env
["MALLOC_PERTURB_"] = "85"
531 env
["MALLOC_CHECK_"] = "3"
532 env
["G_SLICE"] = "debug-blocks"
534 def get_suppression_files():
536 if sys
.version_info
[0] == 2:
537 files
.append(os
.path
.join(
538 sys
.prefix
, "lib", "valgrind", "python.supp"))
540 files
.append(os
.path
.join(
541 sys
.prefix
, "lib", "valgrind", "python3.supp"))
542 files
.append(os
.path
.join(
543 sys
.prefix
, "share", "glib-2.0", "valgrind", "glib.supp"))
544 return [f
for f
in files
if os
.path
.isfile(f
)]
549 env
["G_SLICE"] = "always-malloc"
550 env
["G_DEBUG"] = "gc-friendly"
551 env
["PYTHONMALLOC"] = "malloc"
554 "valgrind", "--leak-check=full", "--show-possibly-lost=no",
555 "--num-callers=20", "--child-silent-after-fork=yes",
556 ] + ["--suppressions=" + f
for f
in get_suppression_files()]
558 if self
.valgrind_log_file
:
559 pre_args
+= ["--log-file=" + self
.valgrind_log_file
]
562 env
["PYGI_TEST_GDB"] = "1"
563 pre_args
+= ["gdb", "--args"]
566 log
.info(" ".join(pre_args
))
568 tests_dir
= os
.path
.join(get_script_dir(), "tests")
569 sys
.exit(subprocess
.call(pre_args
+ [
571 os
.path
.join(tests_dir
, "runtests.py"),
575 class quality(Command
):
576 description
= "run code quality tests"
579 def initialize_options(self
):
582 def finalize_options(self
):
586 status
= subprocess
.call([
587 sys
.executable
, "-m", "flake8",
588 ], cwd
=get_script_dir())
590 raise SystemExit(status
)
593 def get_script_dir():
594 return os
.path
.dirname(os
.path
.realpath(__file__
))
597 def get_pycairo_include_dir():
598 """Returns the best guess at where to find the pycairo headers.
599 A bit convoluted because we have to deal with multiple pycairo
602 Raises if pycairo isn't found or it's too old.
605 script_dir
= get_script_dir()
606 pkg_config_name
= get_pycairo_pkg_config_name()
607 min_version
= get_version_requirement(script_dir
, pkg_config_name
)
609 def check_path(include_dir
):
610 log
.info("pycairo: trying include directory: %r" % include_dir
)
611 header_path
= os
.path
.join(include_dir
, "%s.h" % pkg_config_name
)
612 if os
.path
.exists(header_path
):
613 log
.info("pycairo: found %r" % header_path
)
615 log
.info("pycairo: header file (%r) not found" % header_path
)
618 def find_path(paths
):
619 for p
in reversed(paths
):
624 log
.info("pycairo: new API")
627 pkg_version
= pkg_resources
.parse_version(cairo
.version
)
628 pkg_min_version
= pkg_resources
.parse_version(min_version
)
629 if pkg_version
< pkg_min_version
:
630 raise DistutilsSetupError(
631 "pycairo >=%s required, %s found." % (
632 pkg_min_version
, pkg_version
))
634 if hasattr(cairo
, "get_include"):
635 return [cairo
.get_include()]
636 log
.info("pycairo: no get_include()")
640 log
.info("pycairo: old API")
641 dist
= pkg_resources
.get_distribution("pycairo>=%s" % min_version
)
642 log
.info("pycairo: found %r" % dist
)
644 def samefile(src
, dst
):
645 # Python 2 on Windows doesn't have os.path.samefile, so we have to
647 if hasattr(os
.path
, "samefile"):
648 return os
.path
.samefile(src
, dst
)
651 return (os
.path
.normcase(os
.path
.abspath(src
)) ==
652 os
.path
.normcase(os
.path
.abspath(dst
)))
654 def get_sys_path(dist
, name
):
655 # Returns the sysconfig path for a distribution, or None
656 location
= dist
.location
657 for scheme
in sysconfig
.get_scheme_names():
658 for path_type
in ["platlib", "purelib"]:
659 path
= sysconfig
.get_path(path_type
, scheme
)
661 if samefile(path
, location
):
662 return sysconfig
.get_path(name
, scheme
)
663 except EnvironmentError:
666 data_path
= get_sys_path(dist
, "data") or sys
.prefix
667 return [os
.path
.join(data_path
, "include", "pycairo")]
669 def find_pkg_config():
670 log
.info("pycairo: pkg-config")
671 pkg_config_version_check(pkg_config_name
, min_version
)
672 return pkg_config_parse("--cflags-only-I", pkg_config_name
)
674 # First the new get_include() API added in >1.15.6
675 include_dir
= find_path(find_new_api())
676 if include_dir
is not None:
679 # Then try to find it in the data prefix based on the module path.
680 # This works with many virtualenv/userdir setups, but not all apparently,
681 # see https://gitlab.gnome.org/GNOME/pygobject/issues/150
682 include_dir
= find_path(find_old_api())
683 if include_dir
is not None:
686 # Finally, fall back to pkg-config
687 include_dir
= find_path(find_pkg_config())
688 if include_dir
is not None:
691 raise DistutilsSetupError("Could not find pycairo headers")
694 def add_ext_pkg_config_dep(ext
, compiler_type
, name
):
695 script_dir
= get_script_dir()
698 "glib-2.0": ["glib-2.0"],
699 "gio-2.0": ["gio-2.0", "gobject-2.0", "glib-2.0"],
700 "gobject-introspection-1.0":
701 ["girepository-1.0", "gobject-2.0", "glib-2.0"],
704 ["cairo-gobject", "cairo", "gobject-2.0", "glib-2.0"],
708 fallback_libs
= msvc_libraries
[name
]
709 if compiler_type
== "msvc":
710 # assume that INCLUDE and LIB contains the right paths
711 ext
.libraries
+= fallback_libs
713 min_version
= get_version_requirement(script_dir
, name
)
714 pkg_config_version_check(name
, min_version
)
715 ext
.include_dirs
+= pkg_config_parse("--cflags-only-I", name
)
716 ext
.library_dirs
+= pkg_config_parse("--libs-only-L", name
)
717 ext
.libraries
+= pkg_config_parse("--libs-only-l", name
)
720 def add_ext_warn_flags(ext
, compiler
, _cache
={}):
721 cache_key
= compiler
.compiler
[0]
722 if cache_key
not in _cache
:
728 # "-Wdeclaration-after-statement",
729 # "-Wdouble-promotion",
730 "-Wduplicated-branches",
731 # "-Wduplicated-cond",
734 "-Wformat-nonliteral",
736 "-Wimplicit-function-declaration",
739 # "-Wjump-misses-init",
741 "-Wmissing-declarations",
742 "-Wmissing-format-attribute",
743 "-Wmissing-include-dirs",
744 "-Wmissing-noreturn",
745 "-Wmissing-prototypes",
747 # "-Wnull-dereference",
748 "-Wold-style-definition",
757 "-Wstrict-prototypes",
760 "-Wunused-but-set-variable",
765 "-Wno-incompatible-pointer-types-discards-qualifiers",
766 "-Wno-missing-field-initializers",
767 "-Wno-unused-parameter",
768 "-Wno-discarded-qualifiers",
771 # silence clang for unused gcc CFLAGS added by Debian
773 "-Wno-unused-command-line-argument",
776 _cache
[cache_key
] = filter_compiler_arguments(compiler
, args
)
778 ext
.extra_compile_args
+= _cache
[cache_key
]
781 du_build_ext
= get_command_class("build_ext")
784 class build_ext(du_build_ext
):
786 def initialize_options(self
):
787 du_build_ext
.initialize_options(self
)
788 self
.compiler_type
= None
790 def finalize_options(self
):
791 du_build_ext
.finalize_options(self
)
792 self
.compiler_type
= new_compiler(compiler
=self
.compiler
).compiler_type
794 def _write_config_h(self
):
795 script_dir
= get_script_dir()
796 target
= os
.path
.join(script_dir
, "config.h")
797 versions
= parse_versions(script_dir
)
798 with io
.open(target
, 'w', encoding
="utf-8") as h
:
800 /* Configuration header created by setup.py - do not edit */
804 #define PYGOBJECT_MAJOR_VERSION %(PYGOBJECT_MAJOR_VERSION)s
805 #define PYGOBJECT_MINOR_VERSION %(PYGOBJECT_MINOR_VERSION)s
806 #define PYGOBJECT_MICRO_VERSION %(PYGOBJECT_MICRO_VERSION)s
807 #define VERSION "%(VERSION)s"
809 #endif /* _CONFIG_H */
812 def _setup_extensions(self
):
813 ext
= {e
.name
: e
for e
in self
.extensions
}
815 compiler
= new_compiler(compiler
=self
.compiler
)
816 customize_compiler(compiler
)
818 def add_dependency(ext
, name
):
819 add_ext_pkg_config_dep(ext
, compiler
.compiler_type
, name
)
821 def add_pycairo(ext
):
822 ext
.include_dirs
+= [get_pycairo_include_dir()]
824 gi_ext
= ext
["gi._gi"]
825 add_dependency(gi_ext
, "glib-2.0")
826 add_dependency(gi_ext
, "gio-2.0")
827 add_dependency(gi_ext
, "gobject-introspection-1.0")
828 add_dependency(gi_ext
, "libffi")
829 add_ext_warn_flags(gi_ext
, compiler
)
831 gi_cairo_ext
= ext
["gi._gi_cairo"]
832 add_dependency(gi_cairo_ext
, "glib-2.0")
833 add_dependency(gi_cairo_ext
, "gio-2.0")
834 add_dependency(gi_cairo_ext
, "gobject-introspection-1.0")
835 add_dependency(gi_cairo_ext
, "libffi")
836 add_dependency(gi_cairo_ext
, "cairo")
837 add_dependency(gi_cairo_ext
, "cairo-gobject")
838 add_pycairo(gi_cairo_ext
)
839 add_ext_warn_flags(gi_cairo_ext
, compiler
)
842 self
._write
_config
_h
()
843 self
._setup
_extensions
()
844 du_build_ext
.run(self
)
847 class install_pkgconfig(Command
):
848 description
= "install .pc file"
851 def initialize_options(self
):
852 self
.install_base
= None
853 self
.install_platbase
= None
854 self
.install_data
= None
855 self
.compiler_type
= None
858 def finalize_options(self
):
859 self
.set_undefined_options(
861 ('install_base', 'install_base'),
862 ('install_data', 'install_data'),
863 ('install_platbase', 'install_platbase'),
866 self
.set_undefined_options(
868 ('compiler_type', 'compiler_type'),
871 def get_outputs(self
):
874 def get_inputs(self
):
878 cmd
= self
.distribution
.get_command_obj("bdist_wheel", create
=False)
881 "Python wheels and pkg-config is not compatible. "
882 "No pkg-config file will be included in the wheel. Install "
883 "from source if you need one.")
886 if self
.compiler_type
== "msvc":
889 script_dir
= get_script_dir()
890 pkgconfig_in
= os
.path
.join(script_dir
, "pygobject-3.0.pc.in")
891 with io
.open(pkgconfig_in
, "r", encoding
="utf-8") as h
:
895 "prefix": self
.install_base
,
896 "exec_prefix": self
.install_platbase
,
897 "includedir": "${prefix}/include",
898 "datarootdir": "${prefix}/share",
899 "datadir": "${datarootdir}",
900 "VERSION": self
.distribution
.get_version(),
902 for key
, value
in config
.items():
903 content
= content
.replace("@%s@" % key
, value
)
905 libdir
= os
.path
.dirname(get_python_lib(True, True, self
.install_data
))
906 pkgconfig_dir
= os
.path
.join(libdir
, "pkgconfig")
907 self
.mkpath(pkgconfig_dir
)
908 target
= os
.path
.join(pkgconfig_dir
, "pygobject-3.0.pc")
909 with io
.open(target
, "w", encoding
="utf-8") as h
:
911 self
.outfiles
.append(target
)
914 du_install
= get_command_class("install")
917 class install(du_install
):
919 sub_commands
= du_install
.sub_commands
+ [
920 ("install_pkgconfig", lambda self
: True),
925 script_dir
= get_script_dir()
926 pkginfo
= parse_pkg_info(script_dir
)
927 gi_dir
= os
.path
.join(script_dir
, "gi")
930 os
.path
.join("gi", n
) for n
in os
.listdir(gi_dir
)
931 if os
.path
.splitext(n
)[-1] == ".c"
933 cairo_sources
= [os
.path
.join("gi", "pygi-foreign-cairo.c")]
934 for s
in cairo_sources
:
937 readme
= os
.path
.join(script_dir
, "README.rst")
938 with io
.open(readme
, encoding
="utf-8") as h
:
939 long_description
= h
.read()
944 include_dirs
=[script_dir
, gi_dir
],
945 define_macros
=[("HAVE_CONFIG_H", None)],
948 gi_cairo_ext
= Extension(
950 sources
=cairo_sources
,
951 include_dirs
=[script_dir
, gi_dir
],
952 define_macros
=[("HAVE_CONFIG_H", None)],
956 name
=pkginfo
["Name"],
957 version
=pkginfo
["Version"],
958 description
=pkginfo
["Summary"],
959 url
=pkginfo
["Home-page"],
960 author
=pkginfo
["Author"],
961 author_email
=pkginfo
["Author-email"],
962 maintainer
=pkginfo
["Maintainer"],
963 maintainer_email
=pkginfo
["Maintainer-email"],
964 license
=pkginfo
["License"],
965 long_description
=long_description
,
966 platforms
=pkginfo
.get_all("Platform"),
967 classifiers
=pkginfo
.get_all("Classifier"),
979 "build_ext": build_ext
,
980 "distcheck": distcheck
,
981 "build_tests": build_tests
,
985 "install_pkgconfig": install_pkgconfig
,
988 "pycairo>=%s" % get_version_requirement(
989 script_dir
, get_pycairo_pkg_config_name()),
992 ('include/pygobject-3.0', ['gi/pygobject.h']),
998 if __name__
== "__main__":