source: orange/setup.py @ 9757:27213d4e7315

Revision 9757:27213d4e7315, 24.7 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Updated setup.py (new directory structure)

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