setup.py: Remove various classifiers and the download-url which aren't accepted by...
[pygobject.git] / setup.py
blobce75dd59297e91e440999e0a3eefddaf3ef95c01
1 #!/usr/bin/env python
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 """
20 ATTENTION DISTRO PACKAGERS: This is not a valid replacement for autotools.
21 It does not install headers, pkgconfig files and does not support running
22 tests. Its main use case atm is installation in virtualenvs and via pip.
23 """
25 import io
26 import os
27 import re
28 import sys
29 import errno
30 import subprocess
31 import tarfile
32 from email import parser
34 from setuptools import setup, find_packages
35 from distutils.core import Extension, Distribution
36 from distutils.ccompiler import new_compiler
37 from distutils import dir_util
40 def get_command_class(name):
41 # Returns the right class for either distutils or setuptools
42 return Distribution({}).get_command_class(name)
45 def get_pycairo_pkg_config_name():
46 return "py3cairo" if sys.version_info[0] == 3 else "pycairo"
49 def get_version_requirement(conf_dir, pkg_config_name):
50 """Given a pkg-config module name gets the minimum version required"""
52 if pkg_config_name in ["cairo", "cairo-gobject"]:
53 return "0"
55 mapping = {
56 "gobject-introspection-1.0": "introspection",
57 "glib-2.0": "glib",
58 "gio-2.0": "gio",
59 get_pycairo_pkg_config_name(): "pycairo",
60 "libffi": "libffi",
62 assert pkg_config_name in mapping
64 configure_ac = os.path.join(conf_dir, "configure.ac")
65 with io.open(configure_ac, "r", encoding="utf-8") as h:
66 text = h.read()
67 conf_name = mapping[pkg_config_name]
68 res = re.findall(
69 r"%s_required_version,\s*([\d\.]+)\)" % conf_name, text)
70 assert len(res) == 1
71 return res[0]
74 def parse_versions(conf_dir):
75 configure_ac = os.path.join(conf_dir, "configure.ac")
76 with io.open(configure_ac, "r", encoding="utf-8") as h:
77 version = re.findall(r"pygobject_[^\s]+_version,\s*(\d+)\)", h.read())
78 assert len(version) == 3
80 versions = {
81 "PYGOBJECT_MAJOR_VERSION": version[0],
82 "PYGOBJECT_MINOR_VERSION": version[1],
83 "PYGOBJECT_MICRO_VERSION": version[2],
84 "VERSION": ".".join(version),
86 return versions
89 def parse_pkg_info(conf_dir):
90 """Returns an email.message.Message instance containing the content
91 of the PKG-INFO file. The version info is parsed from configure.ac
92 """
94 versions = parse_versions(conf_dir)
96 pkg_info = os.path.join(conf_dir, "PKG-INFO.in")
97 with io.open(pkg_info, "r", encoding="utf-8") as h:
98 text = h.read()
99 for key, value in versions.items():
100 text = text.replace("@%s@" % key, value)
102 p = parser.Parser()
103 message = p.parse(io.StringIO(text))
104 return message
107 def _run_pkg_config(args):
108 command = ["pkg-config"] + args
110 # Add $prefix/share/pkgconfig to PKG_CONFIG_PATH so we use the right
111 # pycairo in case we are in a virtualenv
112 env = dict(os.environ)
113 paths = env.get("PKG_CONFIG_PATH", "").split(os.pathsep)
114 paths = [p for p in paths if p]
115 paths.insert(0, os.path.join(sys.prefix, "share", "pkgconfig"))
116 env["PKG_CONFIG_PATH"] = os.pathsep.join(paths)
118 try:
119 return subprocess.check_output(command, env=env)
120 except OSError as e:
121 if e.errno == errno.ENOENT:
122 raise SystemExit(
123 "%r not found.\nArguments: %r" % (command[0], command))
124 raise SystemExit(e)
125 except subprocess.CalledProcessError as e:
126 raise SystemExit(e)
129 def pkg_config_version_check(pkg, version):
130 _run_pkg_config([
131 "--print-errors",
132 "--exists",
133 '%s >= %s' % (pkg, version),
137 def pkg_config_parse(opt, pkg):
138 ret = _run_pkg_config([opt, pkg])
139 output = ret.decode()
140 opt = opt[-2:]
141 return [x.lstrip(opt) for x in output.split()]
144 du_sdist = get_command_class("sdist")
147 class distcheck(du_sdist):
148 """Creates a tarball and does some additional sanity checks such as
149 checking if the tarballs includes all files and builds.
152 def _check_manifest(self):
153 # make sure MANIFEST.in includes all tracked files
154 assert self.get_archive_files()
156 if subprocess.call(["git", "status"],
157 stdout=subprocess.PIPE,
158 stderr=subprocess.PIPE) != 0:
159 return
161 included_files = self.filelist.files
162 assert included_files
164 process = subprocess.Popen(
165 ["git", "ls-tree", "-r", "HEAD", "--name-only"],
166 stdout=subprocess.PIPE, universal_newlines=True)
167 out, err = process.communicate()
168 assert process.returncode == 0
170 tracked_files = out.splitlines()
171 for ignore in [".gitignore"]:
172 tracked_files.remove(ignore)
174 diff = set(tracked_files) - set(included_files)
175 assert not diff, (
176 "Not all tracked files included in tarball, check MANIFEST.in",
177 diff)
179 def _check_dist(self):
180 # make sure the tarball builds
181 assert self.get_archive_files()
183 distcheck_dir = os.path.join(self.dist_dir, "distcheck")
184 if os.path.exists(distcheck_dir):
185 dir_util.remove_tree(distcheck_dir)
186 self.mkpath(distcheck_dir)
188 archive = self.get_archive_files()[0]
189 tfile = tarfile.open(archive, "r:gz")
190 tfile.extractall(distcheck_dir)
191 tfile.close()
193 name = self.distribution.get_fullname()
194 extract_dir = os.path.join(distcheck_dir, name)
196 old_pwd = os.getcwd()
197 os.chdir(extract_dir)
198 try:
199 self.spawn([sys.executable, "setup.py", "build"])
200 self.spawn([sys.executable, "setup.py", "install",
201 "--root", "../prefix", "--record", "../log.txt"])
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 du_build_ext = get_command_class("build_ext")
214 class build_ext(du_build_ext):
216 def initialize_options(self):
217 du_build_ext.initialize_options(self)
218 self.compiler_type = None
220 def finalize_options(self):
221 du_build_ext.finalize_options(self)
222 self.compiler_type = new_compiler(compiler=self.compiler).compiler_type
224 def _write_config_h(self):
225 script_dir = os.path.dirname(os.path.realpath(__file__))
226 target = os.path.join(script_dir, "config.h")
227 versions = parse_versions(script_dir)
228 with io.open(target, 'w', encoding="utf-8") as h:
229 h.write("""
230 /* Configuration header created by setup.py - do not edit */
231 #ifndef _CONFIG_H
232 #define _CONFIG_H 1
234 #define PYGOBJECT_MAJOR_VERSION %(PYGOBJECT_MAJOR_VERSION)s
235 #define PYGOBJECT_MINOR_VERSION %(PYGOBJECT_MINOR_VERSION)s
236 #define PYGOBJECT_MICRO_VERSION %(PYGOBJECT_MICRO_VERSION)s
237 #define VERSION "%(VERSION)s"
239 #endif /* _CONFIG_H */
240 """ % versions)
242 def _setup_extensions(self):
243 ext = {e.name: e for e in self.extensions}
244 script_dir = os.path.dirname(os.path.realpath(__file__))
246 msvc_libraries = {
247 "glib-2.0": ["glib-2.0"],
248 "gio-2.0": ["gio-2.0", "gobject-2.0", "glib-2.0"],
249 "gobject-introspection-1.0":
250 ["girepository-1.0", "gobject-2.0", "glib-2.0"],
251 get_pycairo_pkg_config_name(): ["cairo"],
252 "cairo": ["cairo"],
253 "cairo-gobject":
254 ["cairo-gobject", "cairo", "gobject-2.0", "glib-2.0"],
255 "libffi": ["ffi"],
258 def add_dependency(ext, name):
259 fallback_libs = msvc_libraries[name]
261 if self.compiler_type == "msvc":
262 # assume that INCLUDE and LIB contains the right paths
263 ext.libraries += fallback_libs
265 # The PyCairo header is installed in a subdir of the
266 # Python installation that we are building for, so
267 # deduce that include path here, and use it
268 ext.include_dirs += [
269 os.path.join(sys.prefix, "include", "pycairo")]
270 else:
271 min_version = get_version_requirement(script_dir, name)
272 pkg_config_version_check(name, min_version)
273 ext.include_dirs += pkg_config_parse("--cflags-only-I", name)
274 ext.library_dirs += pkg_config_parse("--libs-only-L", name)
275 ext.libraries += pkg_config_parse("--libs-only-l", name)
277 gi_ext = ext["gi._gi"]
278 add_dependency(gi_ext, "glib-2.0")
279 add_dependency(gi_ext, "gio-2.0")
280 add_dependency(gi_ext, "gobject-introspection-1.0")
281 add_dependency(gi_ext, "libffi")
283 gi_cairo_ext = ext["gi._gi_cairo"]
284 add_dependency(gi_cairo_ext, "glib-2.0")
285 add_dependency(gi_cairo_ext, "gio-2.0")
286 add_dependency(gi_cairo_ext, "gobject-introspection-1.0")
287 add_dependency(gi_cairo_ext, "libffi")
288 add_dependency(gi_cairo_ext, "cairo")
289 add_dependency(gi_cairo_ext, "cairo-gobject")
290 add_dependency(gi_cairo_ext, get_pycairo_pkg_config_name())
292 def run(self):
293 self._write_config_h()
294 self._setup_extensions()
295 du_build_ext.run(self)
298 def main():
299 script_dir = os.path.dirname(os.path.realpath(__file__))
300 pkginfo = parse_pkg_info(script_dir)
301 gi_dir = os.path.join(script_dir, "gi")
303 sources = [
304 os.path.join("gi", n) for n in os.listdir(gi_dir)
305 if os.path.splitext(n)[-1] == ".c"
307 cairo_sources = [os.path.join("gi", "pygi-foreign-cairo.c")]
308 for s in cairo_sources:
309 sources.remove(s)
311 gi_ext = Extension(
312 name='gi._gi',
313 sources=sources,
314 include_dirs=[script_dir, gi_dir],
315 define_macros=[("HAVE_CONFIG_H", None)],
318 gi_cairo_ext = Extension(
319 name='gi._gi_cairo',
320 sources=cairo_sources,
321 include_dirs=[script_dir, gi_dir],
322 define_macros=[("HAVE_CONFIG_H", None)],
325 setup(
326 name=pkginfo["Name"],
327 version=pkginfo["Version"],
328 description=pkginfo["Summary"],
329 url=pkginfo["Home-page"],
330 author=pkginfo["Author"],
331 author_email=pkginfo["Author-email"],
332 maintainer=pkginfo["Maintainer"],
333 maintainer_email=pkginfo["Maintainer-email"],
334 license=pkginfo["License"],
335 long_description=pkginfo["Description"],
336 platforms=pkginfo.get_all("Platform"),
337 classifiers=pkginfo.get_all("Classifier"),
338 packages=find_packages(script_dir),
339 ext_modules=[
340 gi_ext,
341 gi_cairo_ext,
343 cmdclass={
344 "build_ext": build_ext,
345 "distcheck": distcheck,
347 install_requires=[
348 "pycairo>=%s" % get_version_requirement(
349 script_dir, get_pycairo_pkg_config_name()),
354 if __name__ == "__main__":
355 main()