source: orange/setup.py @ 10360:afb1671a63c2

Revision 10360:afb1671a63c2, 26.2 KB checked in by Ales Erjavec <ales.erjavec@…>, 2 years ago (diff)

Removed setupegg.py script

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