source: pyyaml/trunk/setup.py @ 331

Revision 331, 11.6 KB checked in by xi, 5 years ago (diff)

Use Cython if available; added Python 3 support to _yaml.pyx.

Line 
1
2NAME = 'PyYAML'
3VERSION = '3.07'
4DESCRIPTION = "YAML parser and emitter for Python"
5LONG_DESCRIPTION = """\
6YAML is a data serialization format designed for human readability and
7interaction with scripting languages.  PyYAML is a YAML parser and
8emitter for Python.
9
10PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
11support, capable extension API, and sensible error messages.  PyYAML
12supports standard YAML tags and provides Python-specific tags that allow
13to represent an arbitrary Python object.
14
15PyYAML is applicable for a broad range of tasks from complex
16configuration files to object serialization and persistance."""
17AUTHOR = "Kirill Simonov"
18AUTHOR_EMAIL = 'xi@resolvent.net'
19LICENSE = "MIT"
20PLATFORMS = "Any"
21URL = "http://pyyaml.org/wiki/PyYAML"
22DOWNLOAD_URL = "http://pyyaml.org/download/pyyaml/%s-%s.tar.gz" % (NAME, VERSION)
23CLASSIFIERS = [
24    "Development Status :: 5 - Production/Stable",
25    "Intended Audience :: Developers",
26    "License :: OSI Approved :: MIT License",
27    "Operating System :: OS Independent",
28    "Programming Language :: Python",
29    "Programming Language :: Python :: 2",
30    "Programming Language :: Python :: 2.3",
31    "Programming Language :: Python :: 2.4",
32    "Programming Language :: Python :: 2.5",
33    "Programming Language :: Python :: 2.6",
34    "Programming Language :: Python :: 3",
35    "Programming Language :: Python :: 3.0",
36    "Topic :: Software Development :: Libraries :: Python Modules",
37    "Topic :: Text Processing :: Markup",
38]
39
40
41LIBYAML_CHECK = """
42#include <yaml.h>
43
44int main(void) {
45    yaml_parser_t parser;
46    yaml_emitter_t emitter;
47
48    yaml_parser_initialize(&parser);
49    yaml_parser_delete(&parser);
50
51    yaml_emitter_initialize(&emitter);
52    yaml_emitter_delete(&emitter);
53
54    return 0;
55}
56"""
57
58
59import sys, os.path
60
61from distutils import log
62from distutils.core import setup, Command
63from distutils.core import Distribution as _Distribution
64from distutils.core import Extension as _Extension
65from distutils.dir_util import mkpath
66from distutils.command.build_ext import build_ext as _build_ext
67from distutils.command.bdist_rpm import bdist_rpm as _bdist_rpm
68from distutils.errors import CompileError, LinkError, DistutilsPlatformError
69
70if 'setuptools.extension' in sys.modules:
71    _Extension = sys.modules['setuptools.extension']._Extension
72    sys.modules['distutils.core'].Extension = _Extension
73    sys.modules['distutils.extension'].Extension = _Extension
74    sys.modules['distutils.command.build_ext'].Extension = _Extension
75
76with_pyrex = None
77if sys.version_info[0] < 3:
78    try:
79        from Cython.Distutils.extension import Extension as _Extension
80        from Cython.Distutils import build_ext as _build_ext
81        with_pyrex = 'cython'
82    except ImportError:
83        try:
84            from Pyrex.Distutils import Extension as _Extension
85            from Pyrex.Distutils import build_ext as _build_ext
86            with_pyrex = 'pyrex'
87        except ImportError:
88            pass
89
90
91class Distribution(_Distribution):
92
93    def __init__(self, attrs=None):
94        _Distribution.__init__(self, attrs)
95        if not self.ext_modules:
96            return
97        for idx in range(len(self.ext_modules)-1, -1, -1):
98            ext = self.ext_modules[idx]
99            if not isinstance(ext, Extension):
100                continue
101            setattr(self, ext.attr_name, None)
102            self.global_options = [
103                    (ext.option_name, None,
104                        "include %s (default if %s is available)"
105                        % (ext.feature_description, ext.feature_name)),
106                    (ext.neg_option_name, None,
107                        "exclude %s" % ext.feature_description),
108            ] + self.global_options
109            self.negative_opt = self.negative_opt.copy()
110            self.negative_opt[ext.neg_option_name] = ext.option_name
111
112    def has_ext_modules(self):
113        if not self.ext_modules:
114            return False
115        for ext in self.ext_modules:
116            with_ext = self.ext_status(ext)
117            if with_ext is None or with_ext:
118                return True
119        return False
120
121    def ext_status(self, ext):
122        if isinstance(ext, Extension):
123            with_ext = getattr(self, ext.attr_name)
124            return with_ext
125        else:
126            return True
127
128
129class Extension(_Extension):
130
131    def __init__(self, name, sources, feature_name, feature_description,
132            feature_check, **kwds):
133        if not with_pyrex:
134            for filename in sources[:]:
135                base, ext = os.path.splitext(filename)
136                if ext == '.pyx':
137                    sources.remove(filename)
138                    sources.append('%s.c' % base)
139        _Extension.__init__(self, name, sources, **kwds)
140        self.feature_name = feature_name
141        self.feature_description = feature_description
142        self.feature_check = feature_check
143        self.attr_name = 'with_' + feature_name.replace('-', '_')
144        self.option_name = 'with-' + feature_name
145        self.neg_option_name = 'without-' + feature_name
146
147
148class build_ext(_build_ext):
149
150    def run(self):
151        optional = True
152        disabled = True
153        for ext in self.extensions:
154            with_ext = self.distribution.ext_status(ext)
155            if with_ext is None:
156                disabled = False
157            elif with_ext:
158                optional = False
159                disabled = False
160                break
161        if disabled:
162            return
163        try:
164            _build_ext.run(self)
165        except DistutilsPlatformError:
166            exc = sys.exc_info()[1]
167            if optional:
168                log.warn(str(exc))
169                log.warn("skipping build_ext")
170            else:
171                raise
172
173    def get_source_files(self):
174        self.check_extensions_list(self.extensions)
175        filenames = []
176        for ext in self.extensions:
177            if with_pyrex == 'pyrex':
178                self.pyrex_sources(ext.sources, ext)
179            elif with_pyrex == 'cython':
180                self.cython_sources(ext.sources, ext)
181            for filename in ext.sources:
182                filenames.append(filename)
183                base = os.path.splitext(filename)[0]
184                for ext in ['c', 'h', 'pyx', 'pxd']:
185                    filename = '%s.%s' % (base, ext)
186                    if filename not in filenames and os.path.isfile(filename):
187                        filenames.append(filename)
188        return filenames
189
190    def get_outputs(self):
191        self.check_extensions_list(self.extensions)
192        outputs = []
193        for ext in self.extensions:
194            fullname = self.get_ext_fullname(ext.name)
195            filename = os.path.join(self.build_lib,
196                                    self.get_ext_filename(fullname))
197            if os.path.isfile(filename):
198                outputs.append(filename)
199        return outputs
200
201    def build_extensions(self):
202        self.check_extensions_list(self.extensions)
203        for ext in self.extensions:
204            with_ext = self.distribution.ext_status(ext)
205            if with_ext is None:
206                with_ext = self.check_extension_availability(ext)
207            if not with_ext:
208                continue
209            if with_pyrex == 'pyrex':
210                ext.sources = self.pyrex_sources(ext.sources, ext)
211            elif with_pyrex == 'cython':
212                ext.sources = self.cython_sources(ext.sources, ext)
213            self.build_extension(ext)
214
215    def check_extension_availability(self, ext):
216        cache = os.path.join(self.build_temp, 'check_%s.out' % ext.feature_name)
217        if not self.force and os.path.isfile(cache):
218            data = open(cache).read().strip()
219            if data == '1':
220                return True
221            elif data == '0':
222                return False
223        mkpath(self.build_temp)
224        src = os.path.join(self.build_temp, 'check_%s.c' % ext.feature_name)
225        open(src, 'w').write(ext.feature_check)
226        log.info("checking if %s is compilable" % ext.feature_name)
227        try:
228            [obj] = self.compiler.compile([src],
229                    macros=ext.define_macros+[(undef,) for undef in ext.undef_macros],
230                    include_dirs=ext.include_dirs,
231                    extra_postargs=(ext.extra_compile_args or []),
232                    depends=ext.depends)
233        except CompileError:
234            log.warn("")
235            log.warn("%s is not found or a compiler error: forcing --%s"
236                     % (ext.feature_name, ext.neg_option_name))
237            log.warn("(if %s is installed correctly, you may need to"
238                    % ext.feature_name)
239            log.warn(" specify the option --include-dirs or uncomment and")
240            log.warn(" modify the parameter include_dirs in setup.cfg)")
241            open(cache, 'w').write('0\n')
242            return False
243        prog = 'check_%s' % ext.feature_name
244        log.info("checking if %s is linkable" % ext.feature_name)
245        try:
246            self.compiler.link_executable([obj], prog,
247                    output_dir=self.build_temp,
248                    libraries=ext.libraries,
249                    library_dirs=ext.library_dirs,
250                    runtime_library_dirs=ext.runtime_library_dirs,
251                    extra_postargs=(ext.extra_link_args or []))
252        except LinkError:
253            log.warn("")
254            log.warn("%s is not found or a linker error: forcing --%s"
255                     % (ext.feature_name, ext.neg_option_name))
256            log.warn("(if %s is installed correctly, you may need to"
257                    % ext.feature_name)
258            log.warn(" specify the option --library-dirs or uncomment and")
259            log.warn(" modify the parameter library_dirs in setup.cfg)")
260            open(cache, 'w').write('0\n')
261            return False
262        open(cache, 'w').write('1\n')
263        return True
264
265
266class bdist_rpm(_bdist_rpm):
267
268    def _make_spec_file(self):
269        argv0 = sys.argv[0]
270        features = []
271        for ext in self.distribution.ext_modules:
272            if not isinstance(ext, Extension):
273                continue
274            with_ext = getattr(self.distribution, ext.attr_name)
275            if with_ext is None:
276                continue
277            if with_ext:
278                features.append('--'+ext.option_name)
279            else:
280                features.append('--'+ext.neg_option_name)
281        sys.argv[0] = ' '.join([argv0]+features)
282        spec_file = _bdist_rpm._make_spec_file(self)
283        sys.argv[0] = argv0
284        return spec_file
285
286
287class test(Command):
288
289    user_options = []
290
291    def initialize_options(self):
292        pass
293
294    def finalize_options(self):
295        pass
296
297    def run(self):
298        build_cmd = self.get_finalized_command('build')
299        build_cmd.run()
300        sys.path.insert(0, build_cmd.build_lib)
301        if sys.version_info[0] < 3:
302            sys.path.insert(0, 'tests/lib')
303        else:
304            sys.path.insert(0, 'tests/lib3')
305        import test_all
306        test_all.main([])
307
308
309if __name__ == '__main__':
310
311    package_dir = {
312            '2': 'lib',
313    }
314
315    setup(
316        name=NAME,
317        version=VERSION,
318        description=DESCRIPTION,
319        long_description=LONG_DESCRIPTION,
320        author=AUTHOR,
321        author_email=AUTHOR_EMAIL,
322        license=LICENSE,
323        platforms=PLATFORMS,
324        url=URL,
325        download_url=DOWNLOAD_URL,
326        classifiers=CLASSIFIERS,
327
328        package_dir={'': {2: 'lib', 3: 'lib3'}[sys.version_info[0]]},
329        packages=['yaml'],
330        ext_modules=[
331            Extension('_yaml', ['ext/_yaml.pyx'],
332                'libyaml', "LibYAML bindings", LIBYAML_CHECK,
333                libraries=['yaml']),
334        ],
335
336        distclass=Distribution,
337
338        cmdclass={
339            'build_ext': build_ext,
340            'bdist_rpm': bdist_rpm,
341            'test': test,
342        },
343    )
344
Note: See TracBrowser for help on using the repository browser.