source: orange/setup.py @ 9557:3db591b97b0c

Revision 9557:3db591b97b0c, 23.7 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Fix to make build_ext -i work.

Line 
1#!usr/bin/env python
2
3import os, sys
4have_setuptools = False
5
6if "USE_SETUPTOOLS" in os.environ:
7    try:
8        from setuptools import setup
9        have_setuptools = True
10    except ImportError:
11        have_setuptools = False
12       
13import distutils.core
14from distutils.core import setup
15from distutils.core import Extension
16from distutils.command.build_ext import build_ext
17from distutils.command.install_lib import install_lib
18from distutils.msvccompiler import MSVCCompiler
19from distutils.unixccompiler import UnixCCompiler
20
21import re
22import glob
23
24from subprocess import check_call
25
26import types
27
28from distutils.dep_util import newer_group
29from distutils.file_util import copy_file
30from distutils import log
31
32from distutils.sysconfig import get_python_inc, get_config_var
33import numpy
34numpy_include_dir = numpy.get_include()
35python_include_dir = get_python_inc(plat_specific=1)
36
37include_dirs = [python_include_dir, numpy_include_dir, "source/include"]
38
39if sys.platform == "darwin":
40    extra_compile_args = "-fPIC -fpermissive -fno-common -w -DDARWIN".split()
41    extra_link_args = "-headerpad_max_install_names -undefined dynamic_lookup".split()
42elif sys.platform == "win32":
43    extra_compile_args = ["-EHsc"]
44    extra_link_args = []
45elif sys.platform.startswith("linux"):
46    extra_compile_args = "-fPIC -fpermissive -w -DLINUX".split()
47    extra_link_args = ["-Wl,-R$ORIGIN"]   
48else:
49    extra_compile_args = []
50    extra_link_args = []
51       
52class LibStatic(Extension):
53    pass
54
55class PyXtractExtension(Extension):
56    def __init__(self, *args, **kwargs):
57        for name, default in [("extra_pyxtract_cmds", []), ("lib_type", "dynamic")]:
58            setattr(self, name, kwargs.get(name, default))
59            if name in kwargs:   
60                del kwargs[name]
61           
62        Extension.__init__(self, *args, **kwargs)
63       
64class PyXtractSharedExtension(PyXtractExtension):
65    pass
66       
67class pyxtract_build_ext(build_ext):
68    def run_pyxtract(self, ext, dir):
69        original_dir = os.path.realpath(os.path.curdir)
70        log.info("running pyxtract for %s" % ext.name)
71        try:
72            os.chdir(dir)
73            ## we use the commands which are used for building under windows
74            pyxtract_cmds = [cmd.split() for cmd in getattr(ext, "extra_pyxtract_cmds", [])]
75            if os.path.exists("_pyxtract.bat"): 
76                pyxtract_cmds.extend([cmd.split()[1:] for cmd in open("_pyxtract.bat").read().strip().splitlines()])
77            for cmd in pyxtract_cmds:
78                log.info(" ".join([sys.executable] + cmd))
79                check_call([sys.executable] + cmd)
80            if pyxtract_cmds:
81                ext.include_dirs.append(os.path.join(dir, "ppp"))
82                ext.include_dirs.append(os.path.join(dir, "px"))
83
84        finally:
85            os.chdir(original_dir)
86       
87    def finalize_options(self):
88        build_ext.finalize_options(self)
89        # add the build_lib dir (for liborange_include)
90        if not self.inplace:
91            self.library_dirs.append(self.build_lib) 
92        else:
93            self.library_dirs.append("./orange")
94       
95    def build_extension(self, ext):
96        if isinstance(ext, LibStatic):
97            self.build_static(ext)
98        elif isinstance(ext, PyXtractExtension):
99            self.build_pyxtract(ext)
100        else:
101            build_ext.build_extension(self, ext)
102           
103        if isinstance(ext, PyXtractSharedExtension):
104            if isinstance(self.compiler, MSVCCompiler):
105                # Copy ${TEMP}/orange/orange.lib to ${BUILD}/orange.lib
106                ext_fullpath = self.get_ext_fullpath(ext.name)
107                lib = glob.glob(os.path.join(self.build_temp, "*", "*", ext.name + ".lib"))[0]
108                copy_file(lib, os.path.splitext(ext_fullpath)[0] + ".lib")
109                         
110                #self.get
111            else:
112                # Make lib{name}.so link to {name}.so
113                ext_path = self.get_ext_fullpath(ext.name)
114                ext_path, ext_filename = os.path.split(ext_path)
115                realpath = os.path.realpath(os.curdir)
116                try:
117                    os.chdir(ext_path)
118                    # Get the shared library name
119                    lib_filename = self.compiler.library_filename(ext.name, lib_type="shared")
120                    # Create the link
121                    copy_file(ext_filename, lib_filename, link="sym")
122                except OSError, ex:
123                    log.info("failed to create shared library for %s: %s" % (ext.name, str(ex)))
124                finally:
125                    os.chdir(realpath)
126           
127    def build_pyxtract(self, ext):
128        ## mostly copied from build_extension
129        sources = ext.sources
130        if sources is None or type(sources) not in (types.ListType, types.TupleType):
131            raise DistutilsSetupError, \
132                  ("in 'ext_modules' option (extension '%s'), " +
133                   "'sources' must be present and must be " +
134                   "a list of source filenames") % ext.name
135        sources = list(sources)
136       
137        ext_path = self.get_ext_fullpath(ext.name)
138#        output_dir, _ = os.path.split(ext_path)
139#        lib_filename = self.compiler.library_filename(ext.name, lib_type='static', output_dir=output_dir)
140       
141        depends = sources + ext.depends
142        if not (self.force or newer_group(depends, ext_path, 'newer')):
143            log.debug("skipping '%s' extension (up-to-date)", ext.name)
144            return
145        else:
146            log.info("building '%s' extension", ext.name)
147
148        # First, scan the sources for SWIG definition files (.i), run
149        # SWIG on 'em to create .c files, and modify the sources list
150        # accordingly.
151        sources = self.swig_sources(sources, ext)
152       
153        # Run pyxtract in dir this adds ppp and px dirs to include_dirs
154        dir = os.path.commonprefix([os.path.split(s)[0] for s in ext.sources])
155        self.run_pyxtract(ext, dir)
156
157        # Next, compile the source code to object files.
158
159        # XXX not honouring 'define_macros' or 'undef_macros' -- the
160        # CCompiler API needs to change to accommodate this, and I
161        # want to do one thing at a time!
162
163        # Two possible sources for extra compiler arguments:
164        #   - 'extra_compile_args' in Extension object
165        #   - CFLAGS environment variable (not particularly
166        #     elegant, but people seem to expect it and I
167        #     guess it's useful)
168        # The environment variable should take precedence, and
169        # any sensible compiler will give precedence to later
170        # command line args.  Hence we combine them in order:
171        extra_args = ext.extra_compile_args or []
172
173        macros = ext.define_macros[:]
174        for undef in ext.undef_macros:
175            macros.append((undef,))
176
177        objects = self.compiler.compile(sources,
178                                         output_dir=self.build_temp,
179                                         macros=macros,
180                                         include_dirs=ext.include_dirs,
181                                         debug=self.debug,
182                                         extra_postargs=extra_args,
183                                         depends=ext.depends)
184
185        # XXX -- this is a Vile HACK!
186        #
187        # The setup.py script for Python on Unix needs to be able to
188        # get this list so it can perform all the clean up needed to
189        # avoid keeping object files around when cleaning out a failed
190        # build of an extension module.  Since Distutils does not
191        # track dependencies, we have to get rid of intermediates to
192        # ensure all the intermediates will be properly re-built.
193        #
194        self._built_objects = objects[:]
195
196        # Now link the object files together into a "shared object" --
197        # of course, first we have to figure out all the other things
198        # that go into the mix.
199        if ext.extra_objects:
200            objects.extend(ext.extra_objects)
201        extra_args = ext.extra_link_args or []
202
203        # Detect target language, if not provided
204        language = ext.language or self.compiler.detect_language(sources)
205
206        self.compiler.link_shared_object(
207            objects, ext_path,
208            libraries=self.get_libraries(ext),
209            library_dirs=ext.library_dirs,
210            runtime_library_dirs=ext.runtime_library_dirs,
211            extra_postargs=extra_args,
212            export_symbols=self.get_export_symbols(ext),
213            debug=self.debug,
214            build_temp=self.build_temp,
215            target_lang=language)
216       
217       
218    def build_static(self, ext):
219        ## mostly copied from build_extension, changed
220        sources = ext.sources
221        if sources is None or type(sources) not in (types.ListType, types.TupleType):
222            raise DistutilsSetupError, \
223                  ("in 'ext_modules' option (extension '%s'), " +
224                   "'sources' must be present and must be " +
225                   "a list of source filenames") % ext.name
226        sources = list(sources)
227       
228        ext_path = self.get_ext_fullpath(ext.name)
229        output_dir, _ = os.path.split(ext_path)
230        if not os.path.exists(output_dir): #VSC fails if the dir does not exist
231            os.makedirs(output_dir)
232        lib_filename = self.compiler.library_filename(ext.name, lib_type='static', output_dir=output_dir)
233       
234        depends = sources + ext.depends
235        if not (self.force or newer_group(depends, lib_filename, 'newer')):
236            log.debug("skipping '%s' extension (up-to-date)", ext.name)
237            return
238        else:
239            log.info("building '%s' extension", ext.name)
240
241        # First, scan the sources for SWIG definition files (.i), run
242        # SWIG on 'em to create .c files, and modify the sources list
243        # accordingly.
244        sources = self.swig_sources(sources, ext)
245
246        # Next, compile the source code to object files.
247
248        # XXX not honouring 'define_macros' or 'undef_macros' -- the
249        # CCompiler API needs to change to accommodate this, and I
250        # want to do one thing at a time!
251
252        # Two possible sources for extra compiler arguments:
253        #   - 'extra_compile_args' in Extension object
254        #   - CFLAGS environment variable (not particularly
255        #     elegant, but people seem to expect it and I
256        #     guess it's useful)
257        # The environment variable should take precedence, and
258        # any sensible compiler will give precedence to later
259        # command line args.  Hence we combine them in order:
260        extra_args = ext.extra_compile_args or []
261
262        macros = ext.define_macros[:]
263        for undef in ext.undef_macros:
264            macros.append((undef,))
265
266        objects = self.compiler.compile(sources,
267                                         output_dir=self.build_temp,
268                                         macros=macros,
269                                         include_dirs=ext.include_dirs,
270                                         debug=self.debug,
271                                         extra_postargs=extra_args,
272                                         depends=ext.depends)
273
274        # XXX -- this is a Vile HACK!
275        #
276        # The setup.py script for Python on Unix needs to be able to
277        # get this list so it can perform all the clean up needed to
278        # avoid keeping object files around when cleaning out a failed
279        # build of an extension module.  Since Distutils does not
280        # track dependencies, we have to get rid of intermediates to
281        # ensure all the intermediates will be properly re-built.
282        #
283        self._built_objects = objects[:]
284
285        # Now link the object files together into a "shared object" --
286        # of course, first we have to figure out all the other things
287        # that go into the mix.
288        if ext.extra_objects:
289            objects.extend(ext.extra_objects)
290        extra_args = ext.extra_link_args or []
291
292        # Detect target language, if not provided
293        language = ext.language or self.compiler.detect_language(sources)
294       
295        #first remove old library (ar only appends the contents if archive already exists
296        try:
297            os.remove(lib_filename)
298        except OSError, ex:
299            log.debug("failed to remove obsolete static library %s: %s" %(ext.name, str(ex)))
300
301        self.compiler.create_static_lib(
302            objects, ext.name, output_dir,
303            debug=self.debug,
304            target_lang=language)
305       
306    if not hasattr(build_ext, "get_ext_fullpath"):
307        #On mac OS X python 2.6.1 distutils does not have this method
308        def get_ext_fullpath(self, ext_name):
309            """Returns the path of the filename for a given extension.
310           
311            The file is located in `build_lib` or directly in the package
312            (inplace option).
313            """
314            import string
315            # makes sure the extension name is only using dots
316            all_dots = string.maketrans('/' + os.sep, '..')
317            ext_name = ext_name.translate(all_dots)
318            fullname = self.get_ext_fullname(ext_name)
319            modpath = fullname.split('.')
320            filename = self.get_ext_filename(ext_name)
321            filename = os.path.split(filename)[-1]
322            if not self.inplace:
323                # no further work needed
324                # returning :
325                #   build_dir/package/path/filename
326                filename = os.path.join(*modpath[:-1] + [filename])
327                return os.path.join(self.build_lib, filename)
328            # the inplace option requires to find the package directory
329            # using the build_py command for that
330            package = '.'.join(modpath[0:-1])
331            build_py = self.get_finalized_command('build_py')
332            package_dir = os.path.abspath(build_py.get_package_dir(package))
333            # returning
334            #   package_dir/filename
335            return os.path.join(package_dir, filename)
336       
337       
338class my_install_lib(install_lib):
339    """ An command to install orange (preserves liborange.so -> orange.so symlink)
340    """
341    def run(self):
342        install_lib.run(self)
343       
344    def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, preserve_symlinks=1, level=1):
345        """ Run copy_tree with preserve_symlinks=1 as default
346        """ 
347        install_lib.copy_tree(self, infile, outfile, preserve_mode, preserve_times, preserve_symlinks, level)
348       
349    def install(self):
350        """ Copy build_dir to install_dir
351        """
352        # A Hack to unlink liborange.so -> orange.so if it already exists,
353        # because copy_tree fails to overwrite it
354        #
355        liborange = os.path.join(self.install_dir, "liborange.so")
356        if self.force and os.path.exists(liborange) and os.path.islink(liborange):
357            log.info("unlinking %s -> %s", liborange, os.path.join(self.install_dir, "orange.so"))
358            os.unlink(liborange)
359           
360        return install_lib.install(self)
361       
362           
363def get_source_files(path, ext="cpp", exclude=[]):
364    files = glob.glob(os.path.join(path, "*." + ext))
365    files = [file for file in files if os.path.basename(file) not in exclude]
366    return files
367
368include_ext = LibStatic("orange_include", get_source_files("source/include/"), include_dirs=include_dirs)
369
370
371if sys.platform == "win32": # ?? mingw/cygwin
372    libraries = ["orange_include"]
373else:
374    libraries = ["stdc++", "orange_include"]
375
376
377import ConfigParser
378config = ConfigParser.RawConfigParser()
379config.read("setup-site.cfg")
380
381orange_sources = get_source_files("source/orange/")
382orange_include_dirs = list(include_dirs)
383orange_libraries = list(libraries)
384
385if config.has_option("blas", "library"):
386    # Link external blas library
387    orange_libraries += [config.get("blas", "library")]
388else:
389    orange_sources += get_source_files("source/orange/blas/", "c")
390   
391if config.has_option("R", "library"):
392    # Link external R library (for linpack)
393    orange_libraries += [config.get("R", "library")]
394else:
395    orange_sources += get_source_files("source/orange/linpack/", "c")
396   
397if config.has_option("liblinear", "library"):
398    # Link external LIBLINEAR library
399    orange_libraries += [config.get("liblinear", "library")]
400else:
401    orange_sources += get_source_files("source/orange/liblinear/", "cpp")
402    orange_include_dirs += ["source/orange/liblinear"]
403   
404if config.has_option("libsvm", "library"):
405    # Link external LibSVM library
406    orange_libraries += [config.get("libsvm", "library")]
407else:
408    orange_sources += get_source_files("source/orange/libsvm/", "cpp")
409   
410
411orange_ext = PyXtractSharedExtension("orange", orange_sources,
412                                      include_dirs=orange_include_dirs,
413                                      extra_compile_args = extra_compile_args + ["-DORANGE_EXPORTS"],
414                                      extra_link_args = extra_link_args,
415                                      libraries=orange_libraries,
416                                      extra_pyxtract_cmds = ["../pyxtract/defvectors.py"],
417#                                      depends=["orange/ppp/lists"]
418                                      )
419
420if sys.platform == "darwin":
421    build_shared_cmd = get_config_var("BLDSHARED")
422    if "-bundle" in build_shared_cmd.split(): #dont link liborange.so with orangeom and orangene - MacOS X treats loadable modules and shared libraries different
423        shared_libs = libraries
424    else:
425        shared_libs = libraries + ["orange"]
426else:
427    shared_libs = libraries + ["orange"]
428   
429orangeom_sources = get_source_files("source/orangeom/", exclude=["lib_vectors.cpp"])
430orangeom_libraries = list(shared_libs)
431orangeom_include_dirs = list(include_dirs)
432
433if config.has_option("qhull", "library"):
434    # Link external qhull library
435    orangeom_libraries += [config.get("qhull", "library")]
436else:
437    orangeom_sources += get_source_files("source/orangeom/qhull/", "c")
438    orangeom_include_dirs += ["source/orangeom"]
439
440
441orangeom_ext = PyXtractExtension("orangeom", orangeom_sources,
442                                  include_dirs=orangeom_include_dirs + ["source/orange/"],
443                                  extra_compile_args = extra_compile_args + ["-DORANGEOM_EXPORTS"],
444                                  extra_link_args = extra_link_args,
445                                  libraries=orangeom_libraries,
446                                  )
447
448orangene_ext = PyXtractExtension("orangene", get_source_files("source/orangene/", exclude=["lib_vectors.cpp"]), 
449                                  include_dirs=include_dirs + ["source/orange/"], 
450                                  extra_compile_args = extra_compile_args + ["-DORANGENE_EXPORTS"],
451                                  extra_link_args = extra_link_args,
452                                  libraries=shared_libs,
453                                  )
454
455corn_ext = Extension("corn", get_source_files("source/corn/"), 
456                     include_dirs=include_dirs + ["source/orange/"], 
457                     extra_compile_args = extra_compile_args + ["-DCORN_EXPORTS"],
458                     extra_link_args = extra_link_args,
459                     libraries=libraries
460                     )
461
462statc_ext = Extension("statc", get_source_files("source/statc/"), 
463                      include_dirs=include_dirs + ["source/orange/"], 
464                      extra_compile_args = extra_compile_args + ["-DSTATC_EXPORTS"],
465                      extra_link_args = extra_link_args,
466                      libraries=libraries
467                      )
468
469import fnmatch
470matches = []
471os.chdir("orange")
472for root, dirnames, filenames in os.walk('Orange'): #Recursively find '__init__.py's
473  for filename in fnmatch.filter(filenames, '__init__.py'):
474      matches.append(os.path.join(root, filename))
475packages = [os.path.dirname(pkg).replace(os.path.sep, '.') for pkg in matches]
476os.chdir("..")
477
478if have_setuptools:
479    setuptools_args = {"zip_safe": False,
480                       "install_requires": ["numpy"],
481                       "extra_requires": ["networkx", "PyQt4", "PyQwt"]
482                       }
483else:
484    setuptools_args = {}
485
486setup(cmdclass={"build_ext": pyxtract_build_ext, "install_lib": my_install_lib},
487      name ="Orange",
488      version = "2.5a1",
489      description = "Orange data mining library for python.",
490      author = "Bioinformatics Laboratory, FRI UL",
491      author_email = "orange@fri.uni-lj.si",
492      maintainer = "Ales Erjavec",
493      maintainer_email = "ales.erjavec@fri.uni-lj.si",
494      url = "http://orange.biolab.si",
495      download_url = "http://orange.biolab.si/svn/orange/trunk",
496      packages = packages + ["",
497                             "OrangeCanvas",
498                             "OrangeWidgets", 
499                             "OrangeWidgets.Associate",
500                             "OrangeWidgets.Classify",
501                             "OrangeWidgets.Data",
502                             "OrangeWidgets.Evaluate",
503                             "OrangeWidgets.Prototypes",
504                             "OrangeWidgets.Regression",
505                             "OrangeWidgets.Unsupervised",
506                             "OrangeWidgets.Visualize",
507                             "OrangeWidgets.Visualize Qt",
508                             "OrangeWidgets.plot",
509                             "OrangeWidgets.plot.primitives",
510                             "doc",
511                             ],
512      package_dir = {"": "orange"},
513      package_data = {"OrangeCanvas": ["icons/*.png", "orngCanvas.pyw", "WidgetTabs.txt"],
514                      "OrangeWidgets": ["icons/*.png", "icons/backgrounds/*.png", "report/index.html"],
515                      "OrangeWidgets.Associate": ["icons/*.png"],
516                      "OrangeWidgets.Classify": ["icons/*.png"],
517                      "OrangeWidgets.Data": ["icons/*.png"],
518                      "OrangeWidgets.Evaluate": ["icons/*.png"],
519                      "OrangeWidgets.Prototypes": ["icons/*.png"],
520                      "OrangeWidgets.Regression": ["icons/*.png"],
521                      "OrangeWidgets.Unsupervised": ["icons/*.png"],
522                      "OrangeWidgets.Visualize": ["icons/*.png"],
523                      "OrangeWidgets.plot": ["*.gs", "*.vs"],
524                      "OrangeWidgets.plot.primitives": ["*.obj"],
525                      "doc": ["datasets/*.tab", ],
526                      "": ["orangerc.cfg"]
527                      },
528      ext_modules = [include_ext, orange_ext, orangeom_ext, orangene_ext, corn_ext, statc_ext],
529      extra_path=("orange", "orange"),
530      scripts = ["orange/orange-canvas"],
531      license = "GNU General Public License (GPL)",
532      keywords = ["data mining", "machine learning", "artificial intelligence"],
533      classifiers = ["Development Status :: 4 - Beta",
534                     "Programming Language :: Python",
535                     "License :: OSI Approved :: GNU General Public License (GPL)",
536                     "Operating System :: POSIX",
537                     "Operating System :: Microsoft :: Windows",
538                     "Topic :: Scientific/Engineering :: Artificial Intelligence",
539                     "Topic :: Scientific/Engineering :: Visualization",
540                     "Intended Audience :: Education",
541                     "Intended Audience :: Science/Research"
542                     ],
543      long_description="""\
544Orange data mining library
545==========================
546
547Orange is a scriptable environment for fast prototyping of new
548algorithms and testing schemes. It is a collection of Python packages
549that sit over the core library and implement some functionality for
550which execution time is not crucial and which is easier done in Python
551than in C++. This includes a variety of tasks such as attribute subset,
552bagging and boosting, and alike.
553
554Orange also includes a set of graphical widgets that use methods from
555core library and Orange modules. Through visual programming, widgets
556can be assembled together into an application by a visual programming
557tool called Orange Canvas.
558""",
559      **setuptools_args)
560     
561
Note: See TracBrowser for help on using the repository browser.