source: indico/setup.py @ ec893c

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.99v1.0v1.1
Last change on this file since ec893c was a09404, checked in by Pedro Ferreira <jose.pedro.ferreira@…>, 15 months ago

[FIX] Made tests_require work

  • Property mode set to 100644
File size: 22.1 KB
Line 
1# -*- coding: utf-8 -*-
2##
3##
4## This file is part of CDS Indico.
5## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
6##
7## CDS Indico is free software; you can redistribute it and/or
8## modify it under the terms of the GNU General Public License as
9## published by the Free Software Foundation; either version 2 of the
10## License, or (at your option) any later version.
11##
12## CDS Indico is distributed in the hope that it will be useful, but
13## WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15## General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20
21# Autoinstalls setuptools if the user doesn't have them already
22import ez_setup
23ez_setup.use_setuptools()
24
25import commands
26import getopt
27import os
28import re
29import shutil
30import string
31import sys
32import itertools
33from distutils.sysconfig import get_python_lib, get_python_version
34from distutils.cmd import Command
35from distutils.command import bdist
36from indico.util import i18n
37
38
39import pkg_resources
40from setuptools.command import develop, install, sdist, bdist_egg, easy_install, test
41from setuptools import setup, find_packages, findall
42
43
44try:
45    from babel.messages import frontend as babel
46    BABEL_PRESENT = True
47except ImportError:
48    BABEL_PRESENT = False
49
50
51DEPENDENCY_URLS = ["http://indico-software.org/wiki/Admin/Installation/IndicoExtras",
52                   "https://github.com/collective/icalendar/tarball/6f899869d462a23d0ebd3f54fb237e8670242bc4#egg=icalendar-3.0"]
53
54DEVELOP_REQUIRES = ['pojson', 'termcolor']
55
56if sys.platform == 'linux2':
57    import pwd
58    import grp
59
60
61class vars(object):
62    '''Variable holder.'''
63    packageDir = None
64    versionVal = 'None'
65    accessuser = None
66    accessgroup = None
67    dbInstalledBySetupPy = False
68    binDir = None
69    documentationDir = None
70    configurationDir = None
71    htdocsDir = None
72
73###  Methods required by setup() ##############################################
74
75def _generateDataPaths(x):
76
77    dataFilesDict = {}
78
79    for (baseDstDir, srcDir) in x:
80        for f in findall(srcDir):
81            dst_dir = os.path.join(baseDstDir,
82                                   os.path.relpath(os.path.dirname(f), srcDir))
83            if dst_dir not in dataFilesDict:
84                dataFilesDict[dst_dir] = []
85            dataFilesDict[dst_dir].append(f)
86
87    dataFiles = []
88    for k, v in dataFilesDict.items():
89        dataFiles.append((k, v))
90
91    return dataFiles
92
93def _getDataFiles(x):
94    """
95    Returns a fully populated data_files ready to be fed to setup()
96
97    WARNING: when creating a bdist_egg we need to include files inside bin,
98    doc, config & htdocs into the egg therefore we cannot fetch indico.conf
99    values directly because they will not refer to the proper place. We
100    include those files in the egg's root folder.
101    """
102
103    # setup expects a list like this (('foo/bar/baz', 'wiki.py'),
104    #                                 ('a/b/c', 'd.jpg'))
105    #
106    # What we do below is transform a list like this:
107    #                                (('foo', 'bar/baz/wiki.py'),
108    #                                 ('a', 'b/c/d.jpg'))
109    #
110    # first into a dict and then into a pallatable form for setuptools.
111
112    # This re will be used to filter out etc/*.conf files and therefore not overwritting them
113    dataFiles = _generateDataPaths((('bin', 'bin'),
114                                    ('doc', 'doc'),
115                                    ('etc', 'etc')))
116    return dataFiles
117
118
119def _getInstallRequires():
120    '''Returns external packages required by Indico
121
122    These are the ones needed for runtime.'''
123
124    base =  ['ZODB3>=3.8', 'pytz', 'zope.index', 'zope.interface',
125             'lxml', 'cds-indico-extras', 'zc.queue', 'python-dateutil<2.0',
126             'pypdf', 'mako>=0.4.1', 'babel', 'icalendar>=3.0', 'pyatom',
127             'simplejson']
128
129    #for Python older than 2.7
130    if sys.version_info[0] <= 2 and sys.version_info[1] < 7:
131        base.append('argparse')
132
133    return base
134
135
136def _versionInit():
137        '''Retrieves the version number from indico/MaKaC/__init__.py and returns it'''
138
139        from indico.MaKaC import __version__
140        v = __version__
141
142        print('Indico %s' % v)
143
144        return v
145
146###  Commands ###########################################################
147class sdist_indico(sdist.sdist):
148    user_options = sdist.sdist.user_options + \
149                   [('version=', None, 'version to distribute')]
150    version = 'dev'
151
152    def run(self):
153        global x
154        sdist.sdist.run(self)
155
156
157class jsdist_indico:
158    def jsCompress(self):
159        from MaKaC.consoleScripts.installBase import jsCompress
160        jsCompress()
161
162
163def _bdist_indico(dataFiles):
164    class bdist_indico(bdist.bdist, jsdist_indico):
165        def run(self):
166            self.jsCompress()
167            compileAllLanguages(self)
168            bdist.bdist.run(self)
169
170    bdist_indico.dataFiles = dataFiles
171    return bdist_indico
172
173
174def _bdist_egg_indico(dataFiles):
175    class bdist_egg_indico(bdist_egg.bdist_egg, jsdist_indico):
176        def run(self):
177            self.jsCompress()
178            compileAllLanguages(self)
179            bdist_egg.bdist_egg.run(self)
180
181    bdist_egg_indico.dataFiles = dataFiles
182    return bdist_egg_indico
183
184
185class jsbuild(Command):
186    description = "minifies and packs javascript files"
187    user_options = []
188    boolean_options = []
189
190    def initialize_options(self):
191        pass
192
193    def finalize_options(self):
194        pass
195
196    def run(self):
197        from MaKaC.consoleScripts.installBase import jsCompress
198        jsCompress()
199
200
201class develop_indico(develop.develop):
202    description = "prepares the current directory for Indico development"
203    user_options = develop.develop.user_options + [('www-uid=', None, "Set user for cache/log/db (typically apache user)"),
204                    ('www-gid=', None, "Set group for cache/log/db (typically apache group)")]
205
206    www_uid = None
207    www_gid = None
208
209    def run(self):
210        # dependencies, links, etc...
211        develop.develop.run(self)
212
213        env = pkg_resources.Environment()
214        easy_install.main(DEVELOP_REQUIRES)
215        env.scan()
216
217        local = 'etc/indico.conf'
218        if os.path.exists(local):
219            print 'Upgrading existing etc/indico.conf..'
220            upgrade_indico_conf(local, 'etc/indico.conf.sample')
221        else:
222            print 'Creating new etc/indico.conf..'
223            shutil.copy('etc/indico.conf.sample', local)
224
225        for f in [x for x in ('etc/zdctl.conf', 'etc/zodb.conf', 'etc/logging.conf') if not os.path.exists(x)]:
226            shutil.copy('%s.sample' % f, f)
227
228        print """\nIndico needs to store some information in the filesystem (database, cache, temporary files, logs...)
229Please specify the directory where you'd like it to be placed.
230(Note that putting it outside of your sourcecode tree is recommended)"""
231        prefixDirDefault = os.path.dirname(os.getcwd())
232        prefixDir = raw_input('[%s]: ' % prefixDirDefault).strip()
233
234        if prefixDir == '':
235            prefixDir = prefixDirDefault
236
237        directories = dict((d, os.path.join(prefixDir, d)) for d in
238                           ['db', 'log', 'tmp', 'cache', 'archive'])
239
240        print 'Creating directories...',
241        for d in directories.values():
242            if not os.path.exists(d):
243                os.makedirs(d)
244        print 'Done!'
245
246        directories['htdocs'] = os.path.join(os.getcwd(), 'indico', 'htdocs')
247        directories['bin'] = os.path.join(os.getcwd(), 'bin')
248        directories['etc'] = os.path.join(os.getcwd(), 'etc')
249        directories['doc'] = os.path.join(os.getcwd(), 'doc')
250
251        self._update_conf_dir_paths(local, directories)
252
253        directories.pop('htdocs') #avoid modifying the htdocs folder permissions (it brings problems with git)
254
255        from MaKaC.consoleScripts.installBase import _databaseText, _findApacheUserGroup, _checkDirPermissions, _updateDbConfigFiles, _updateMaKaCEggCache
256
257        user = ''
258
259        sourcePath = os.getcwd()
260
261        # find the apache user/group
262        user, group = _findApacheUserGroup(self.www_uid, self.www_gid)
263        _checkDirPermissions(directories, dbInstalledBySetupPy=directories['db'], accessuser=user, accessgroup=group)
264
265        _updateDbConfigFiles(directories['db'], directories['log'], os.path.join(sourcePath, 'etc'), directories['tmp'], user)
266
267        _updateMaKaCEggCache(os.path.join(os.path.dirname(__file__), 'indico', 'MaKaC', '__init__.py'), directories['tmp'])
268
269        updateIndicoConfPathInsideMaKaCConfig(os.path.join(os.path.dirname(__file__), ''), 'indico/MaKaC/common/MaKaCConfig.py')
270        compileAllLanguages(self)
271        print '''
272%s
273        ''' % _databaseText('etc')
274
275        if sys.platform == "linux2":
276            # create symlink to legacy MaKaC dir
277            # this is so that the ".egg-link" created by the "develop" command works
278            os.symlink('indico/MaKaC','MaKaC')
279
280    def _update_conf_dir_paths(self, filePath, dirs):
281        fdata = open(filePath).read()
282        for dir in dirs.items():
283            d = dir[1].replace("\\","/") # For Windows users
284            fdata = re.sub('\/opt\/indico\/%s'%dir[0], d, fdata)
285        open(filePath, 'w').write(fdata)
286
287class test_indico(test.test):
288    """
289    Test command for Indico
290    """
291
292    description = "Test Suite Framework"
293    user_options = test.test.user_options + [('specify=', None, "Use nosetests style (file.class:testcase)"),
294                    ('coverage', None, "Output coverage report in html"),
295                    ('unit', None, "Run only Unit tests"),
296                    ('functional', None, "Run only Functional tests"),
297                    ('pylint', None, "Run python source analysis"),
298                    ('jsunit', None, "Run js unit tests"),
299                    ('jslint', None, "Run js source analysis"),
300                    ('jscoverage', None, "Output coverage report in html for js"),
301                    ('jsspecify=', None, "Use js-test-driver style (TestCaseName.testName)"),
302                    ('log=', None, "Log to console, using specified level"),
303                    ('grid', None, "Use Selenium Grid"),
304                    ('xml', None, "XML output"),
305                    ('html', None, "Make an HTML report (when possible)"),
306                    ('record', None, "Record tests (for --functional)"),
307                    ('parallel', None, "Parallel test execution using Selenium Grid (for --functional)"),
308                    ('threads=', None, "Parallel test execution with several threads (for --functional)"),
309                    ('repeat=', None, "Number of repetitions (for --functional)"),
310                    ('silent', None, "Don't output anything in the console, just generate the report"),
311                    ('killself', None, "Kill this script right after the tests finished without waiting for db shutdown.")]
312    boolean_options = []
313
314    specify = None
315    coverage = False
316    unit = False
317    functional = False
318    pylint = False
319    jsunit = False
320    jslint = False
321    jscoverage = False
322    jsspecify = None
323    grid = None
324    silent = False
325    killself = False
326    html = False
327    record = False
328    parallel = False
329    threads = False
330    repeat = False
331    log = False
332    xml = False
333
334    def _wrap(self, func, *params):
335        def wrapped():
336            self.res = func(*params)
337        self.with_project_on_sys_path(wrapped)
338        return self.res
339
340    def finalize_options(self):
341        testsToRun = []
342
343        allTests = ['unit', 'functional']
344
345        for testType in allTests:
346            if getattr(self, testType):
347                testsToRun.append(testType)
348
349        if self.jsspecify and 'jsunit' not in testsToRun:
350            testsToRun.append('jsunit')
351
352        if testsToRun == []:
353            testsToRun = allTests
354        self.testsToRun = testsToRun
355
356    def run(self):
357
358        if self.distribution.install_requires:
359            self.distribution.fetch_build_eggs(self.distribution.install_requires)
360        if self.distribution.tests_require:
361            self.distribution.fetch_build_eggs(self.distribution.tests_require)
362
363        from indico.tests import TestManager
364
365        #missing jars will be downloaded automatically
366        if not self._wrap(self.checkTestJars):
367            print "Some jars could not be downloaded. Please download the missing jars manually"
368            sys.exit(-1)
369
370        options = {'silent': self.silent,
371                   'killself': self.killself,
372                   'html': self.html,
373                   'specify': self.specify,
374                   'coverage': self.coverage,
375                   'record': self.record,
376                   'parallel': self.parallel,
377                   'threads': self.threads,
378                   'repeat': self.repeat,
379                   'log': self.log,
380                   'xml':self.xml}
381
382        # get only options that are active
383        options = dict((k,v) for (k,v) in options.iteritems() if v)
384
385        manager = TestManager()
386        result = self._wrap(manager.main, self.testsToRun, options)
387
388        sys.exit(result)
389
390    def checkTestJars(self):
391        """
392        check if needed jars are here, if not,
393        dowload them and unzip the file if necessary
394        """
395
396        from indico.tests import TestConfig
397
398        jarsList = {}
399        currentFilePath = os.path.dirname(__file__)
400        testModPath = os.path.join(currentFilePath, 'indico', 'tests')
401
402        try:
403            jarsList['jsunit'] = {'path':     os.path.join(testModPath,
404                                                           'javascript',
405                                                           'unit'),
406                                  'url':      TestConfig.getInstance().getJSUnitURL(),
407                                  'filename': TestConfig.getInstance().getJSUnitFilename()}
408
409            jarsList['jscoverage'] = {'path':     os.path.join(testModPath,
410                                                               'javascript',
411                                                               'unit',
412                                                               'plugins'),
413                                      'url':      TestConfig.getInstance().getJSCoverageURL(),
414                                      'filename': TestConfig.getInstance().getJSCoverageFilename()}
415
416            jarsList['selenium'] = {'path':      os.path.join(testModPath,
417                                                              'python',
418                                                              'functional'),
419                                    'url':       TestConfig.getInstance().getSeleniumURL(),
420                                    'filename':  TestConfig.getInstance().getSeleniumFilename()}
421        except KeyError, key:
422            print "[ERR] Please specify a value for %s in tests.conf" % key
423            sys.exit(1)
424
425        validJars = True
426
427        for name in jarsList:
428            jar = jarsList[name]
429            #check if jar is already here
430            if not os.path.exists(os.path.join(jar['path'], jar['filename'])):
431                print "Downloading %s to %s..." % (jar['url'], jar['path'])
432                try:
433                    self.download(jar['url'], jar['path'])
434                except IOError, e:
435                    validJars = validJars and False
436                    print 'Could not download %s from %s (%s)' % (jar['filename'], jar['url'], e)
437
438        return validJars
439
440    def download(self, url, path):
441        """Copy the contents of a file from a given URL
442        to a local file.
443        """
444        import urllib
445        webFile = urllib.urlopen(url)
446        localFile = open(os.path.join(path, url.split('/')[-1]), 'w')
447        localFile.write(webFile.read())
448        webFile.close()
449        localFile.close()
450
451    def unzip(self, zipPath, inZipPath, targetFile):
452        """extract the needed file from zip and then delete the zip"""
453        import zipfile
454        try:
455            zfobj = zipfile.ZipFile(zipPath)
456            outfile = open(targetFile, 'wb')
457            outfile.write(zfobj.read(inZipPath))
458            outfile.flush()
459            outfile.close()
460
461            #delete zip file
462            os.unlink(zipPath)
463        except NameError, e:
464            print e
465
466
467class egg_filename(Command):
468    description = "Get the file name of the generated egg"
469    user_options = []
470    boolean_options = []
471
472    def initialize_options(self):
473        pass
474
475    def finalize_options(self):
476        ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
477        self.egg_info = ei_cmd.egg_info
478
479        basename = pkg_resources.Distribution(
480            None, None, ei_cmd.egg_name, ei_cmd.egg_version,
481            get_python_version(),
482            self.distribution.has_ext_modules() and pkg_utils.get_build_platform
483            ).egg_name()
484
485        print basename
486
487
488    def run(self):
489        pass
490
491
492if __name__ == '__main__':
493    # Always load source from the current folder
494    sys.path = [os.path.abspath('indico')] + sys.path
495
496    #PWD_INDICO_CONF = 'etc/indico.conf'
497    #if not os.path.exists(PWD_INDICO_CONF):
498    #    shutil.copy('etc/indico.conf.sample', PWD_INDICO_CONF)
499
500    from MaKaC.consoleScripts.installBase import *
501
502
503    #Dirty trick: For running tests, we need to load all the modules and get rid of unnecessary outputs
504    tempLoggingDir = None
505    if 'test' in sys.argv:
506        import logging
507        import tempfile
508        tempLoggingDir = tempfile.mkdtemp()
509        logging.basicConfig(filename=os.path.join(tempLoggingDir, 'logging'),
510                            level=logging.DEBUG)
511        setIndicoInstallMode(False)
512    else:
513        setIndicoInstallMode(True)
514
515    x = vars()
516    x.packageDir = os.path.join(get_python_lib(), 'MaKaC')
517
518
519    x.binDir = 'bin'
520    x.documentationDir = 'doc'
521    x.configurationDir = 'etc'
522    x.htdocsDir = 'htdocs'
523
524    dataFiles = _getDataFiles(x)
525
526    foundPackages = list('MaKaC.%s' % pkg for pkg in
527                         find_packages(where = 'indico/MaKaC'))
528    foundPackages.append('MaKaC')
529    foundPackages.append('htdocs')
530
531    # add our namespace package
532    foundPackages += list('indico.%s' % pkg for pkg in
533                         find_packages(where = 'indico',
534                                       exclude = ['htdocs*', 'MaKaC*']))
535    foundPackages.append('indico')
536
537    cmdclass = {'sdist': sdist_indico,
538                'bdist': _bdist_indico(dataFiles),
539                'bdist_egg': _bdist_egg_indico(dataFiles),
540                'jsbuild': jsbuild,
541                'develop_config': develop_indico,
542                'test': test_indico,
543                'egg_filename': egg_filename
544                }
545
546    if BABEL_PRESENT:
547        for cmdname in ['init_catalog', 'extract_messages', 'compile_catalog', 'update_catalog']:
548            cmdclass['%s_js' % cmdname] = getattr(babel, cmdname)
549        cmdclass['compile_catalog_js'] = i18n.generate_messages_js
550
551    setup(name = "indico",
552          cmdclass = cmdclass,
553          version = _versionInit(),
554          description = "Indico is a full-featured conference lifecycle management and meeting/lecture scheduling tool",
555          author = "Indico Team",
556          author_email = "indico-team@cern.ch",
557          url = "http://indico-software.org",
558          download_url = "http://indico-software.org/wiki/Releases/Indico0.98-rc1",
559          platforms = ["any"],
560          long_description = "Indico allows you to schedule conferences, from single talks to complex meetings with sessions and contributions. It also includes an advanced user delegation mechanism, allows paper reviewing, archival of conference information and electronic proceedings",
561          license = "http://www.gnu.org/licenses/gpl-2.0.txt",
562          entry_points = """
563            [console_scripts]
564
565            indico_scheduler = indico.modules.scheduler.daemon_script:main
566            indico_initial_setup = MaKaC.consoleScripts.indicoInitialSetup:main
567            indico_ctl = MaKaC.consoleScripts.indicoCtl:main
568            indico_livesync = indico.ext.livesync.console:main
569            indico_shell = indico.util.shell:main
570
571            [indico.ext_types]
572
573            Collaboration = MaKaC.plugins.Collaboration
574            InstantMessaging = MaKaC.plugins.InstantMessaging
575            RoomBooking = MaKaC.plugins.RoomBooking
576            EPayment = MaKaC.plugins.EPayment
577            livesync = indico.ext.livesync
578            importer = indico.ext.importer
579
580
581            [indico.ext]
582
583            Collaboration.EVO = MaKaC.plugins.Collaboration.EVO
584            Collaboration.Vidyo = MaKaC.plugins.Collaboration.Vidyo
585            Collaboration.CERNMCU = MaKaC.plugins.Collaboration.CERNMCU
586            Collaboration.RecordingManager = MaKaC.plugins.Collaboration.RecordingManager
587            Collaboration.RecordingRequest = MaKaC.plugins.Collaboration.RecordingRequest
588            Collaboration.WebcastRequest = MaKaC.plugins.Collaboration.WebcastRequest
589
590            RoomBooking.CERN = MaKaC.plugins.RoomBooking.CERN
591            RoomBooking.default = MaKaC.plugins.RoomBooking.default
592
593            EPayment.payPal = MaKaC.plugins.EPayment.payPal
594            EPayment.worldPay = MaKaC.plugins.EPayment.worldPay
595            EPayment.yellowPay = MaKaC.plugins.EPayment.yellowPay
596            EPayment.skipjack = MaKaC.plugins.EPayment.skipjack
597
598            importer.invenio = indico.ext.importer.invenio
599            importer.dummy = indico.ext.importer.dummy
600
601            InstantMessaging.XMPP = MaKaC.plugins.InstantMessaging.XMPP
602
603            livesync.invenio = indico.ext.livesync.invenio
604            livesync.cern_search = indico.ext.livesync.cern_search
605
606            """,
607          zip_safe = False,
608          packages = foundPackages,
609          package_dir = { 'indico': 'indico',
610                          'htdocs': os.path.join('indico', 'htdocs'),
611                          'MaKaC' : os.path.join('indico', 'MaKaC')},
612          package_data = {'indico': ['*.*']},
613          include_package_data=True,
614          namespace_packages = ['indico', 'indico.ext'],
615          install_requires = _getInstallRequires(),
616          tests_require = ['nose', 'rednose', 'twill', 'selenium', 'figleaf'],
617          data_files = dataFiles,
618          dependency_links = DEPENDENCY_URLS
619          )
620
621    #delete the temp folder used for logging
622    if 'test' in sys.argv:
623        shutil.rmtree(tempLoggingDir)
Note: See TracBrowser for help on using the repository browser.