source: orange/setup.py @ 10354:806a16e991b5

Revision 10354:806a16e991b5, 26.1 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Updated list of files that get included with install.

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