source: orange/setup.py @ 9893:9e2416dc9a71

Revision 9893:9e2416dc9a71, 25.4 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Fixed a typo

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