[FIX] Error reports
[cds-indico.git] / setup.py
blobc62d6f945e91f1687e437f9346d0bffd35ae19bd
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.
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.
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.
21 # Autoinstalls setuptools if the user doesn't have them already
22 import ez_setup
23 ez_setup.use_setuptools()
25 import commands
26 import getopt
27 import os
28 import re
29 import shutil
30 import string
31 import sys
32 from distutils.sysconfig import get_python_lib, get_python_version
33 from distutils.cmd import Command
34 from distutils.command import bdist
35 from indico.util import i18n
38 import pkg_resources
39 from setuptools.command import develop, install, sdist, bdist_egg, easy_install
40 from setuptools import setup, find_packages, findall
43 try:
44 from babel.messages import frontend as babel
45 BABEL_PRESENT = True
46 except ImportError:
47 BABEL_PRESENT = False
50 EXTRA_RESOURCES_URL = "http://indico-software.org/wiki/Admin/Installation/IndicoExtras"
52 if sys.platform == 'linux2':
53 import pwd
54 import grp
57 class vars(object):
58 '''Variable holder.'''
59 packageDir = None
60 versionVal = 'None'
61 accessuser = None
62 accessgroup = None
63 dbInstalledBySetupPy = False
64 binDir = None
65 documentationDir = None
66 configurationDir = None
67 htdocsDir = None
69 ### Methods required by setup() ##############################################
71 def _generateDataPaths(x):
73 dataFilesDict = {}
75 for (baseDstDir, srcDir) in x:
76 for f in findall(srcDir):
77 dst_dir = os.path.join(baseDstDir,
78 os.path.relpath(os.path.dirname(f), srcDir))
79 if dst_dir not in dataFilesDict:
80 dataFilesDict[dst_dir] = []
81 dataFilesDict[dst_dir].append(f)
83 dataFiles = []
84 for k, v in dataFilesDict.items():
85 dataFiles.append((k, v))
87 return dataFiles
89 def _getDataFiles(x):
90 """
91 Returns a fully populated data_files ready to be fed to setup()
93 WARNING: when creating a bdist_egg we need to include files inside bin,
94 doc, config & htdocs into the egg therefore we cannot fetch indico.conf
95 values directly because they will not refer to the proper place. We
96 include those files in the egg's root folder.
97 """
99 # setup expects a list like this (('foo/bar/baz', 'wiki.py'),
100 # ('a/b/c', 'd.jpg'))
102 # What we do below is transform a list like this:
103 # (('foo', 'bar/baz/wiki.py'),
104 # ('a', 'b/c/d.jpg'))
106 # first into a dict and then into a pallatable form for setuptools.
108 # This re will be used to filter out etc/*.conf files and therefore not overwritting them
109 dataFiles = _generateDataPaths((('bin', 'bin'),
110 ('doc', 'doc'),
111 ('etc', 'etc')))
112 return dataFiles
115 def _getInstallRequires():
116 '''Returns external packages required by Indico
118 These are the ones needed for runtime.'''
120 base = ['ZODB3>=3.8', 'pytz', 'zope.index', 'zope.interface',
121 'lxml', 'cds-indico-extras', 'zc.queue', 'python-dateutil<2.0',
122 'pypdf', 'mako>=0.4.1', 'babel', 'icalendar', 'pyatom',
123 'simplejson']
125 #for Python older than 2.7
126 if sys.version_info[0] <= 2 and sys.version_info[1] < 7:
127 base.append('argparse')
129 return base
132 def _versionInit():
133 '''Retrieves the version number from indico/MaKaC/__init__.py and returns it'''
135 from indico.MaKaC import __version__
136 v = __version__
138 print('Indico %s' % v)
140 return v
142 ### Commands ###########################################################
143 class sdist_indico(sdist.sdist):
144 user_options = sdist.sdist.user_options + \
145 [('version=', None, 'version to distribute')]
146 version = 'dev'
148 def run(self):
149 global x
150 sdist.sdist.run(self)
153 class jsdist_indico:
154 def jsCompress(self):
155 from MaKaC.consoleScripts.installBase import jsCompress
156 jsCompress()
159 def _bdist_indico(dataFiles):
160 class bdist_indico(bdist.bdist, jsdist_indico):
161 def run(self):
162 self.jsCompress()
163 compileAllLanguages(self)
164 bdist.bdist.run(self)
166 bdist_indico.dataFiles = dataFiles
167 return bdist_indico
170 def _bdist_egg_indico(dataFiles):
171 class bdist_egg_indico(bdist_egg.bdist_egg, jsdist_indico):
172 def run(self):
173 self.jsCompress()
174 compileAllLanguages(self)
175 bdist_egg.bdist_egg.run(self)
177 bdist_egg_indico.dataFiles = dataFiles
178 return bdist_egg_indico
181 class jsbuild(Command):
182 description = "minifies and packs javascript files"
183 user_options = []
184 boolean_options = []
186 def initialize_options(self):
187 pass
189 def finalize_options(self):
190 pass
192 def run(self):
193 from MaKaC.consoleScripts.installBase import jsCompress
194 jsCompress()
196 class fetchdeps:
197 def run(self):
198 print "Checking if dependencies need to be installed..."
200 wset = pkg_resources.working_set
202 wset.resolve(map(pkg_resources.Requirement.parse,
203 filter(lambda x: x != None, _getInstallRequires())),
204 installer=self._installMissing)
206 print "Done!"
209 def _installMissing(self, dist):
210 env = pkg_resources.Environment()
211 print dist, EXTRA_RESOURCES_URL
212 easy_install.main(["-f", EXTRA_RESOURCES_URL, "-U", str(dist)])
213 env.scan()
215 if env[str(dist)]:
216 return env[str(dist)][0]
217 else:
218 return None
221 class fetchdeps_indico(fetchdeps, Command):
222 description = "fetch all the dependencies needed to run Indico"
223 user_options = []
224 boolean_options = []
226 def initialize_options(self):
227 pass
229 def finalize_options(self):
230 pass
233 class develop_indico(Command):
234 description = "prepares the current directory for Indico development"
235 user_options = []
236 boolean_options = []
238 def initialize_options(self):
239 pass
241 def finalize_options(self):
242 pass
244 def run(self):
246 fetchdeps().run()
248 local = 'etc/indico.conf'
249 if os.path.exists(local):
250 print 'Upgrading existing etc/indico.conf..'
251 upgrade_indico_conf(local, 'etc/indico.conf.sample')
252 else:
253 print 'Creating new etc/indico.conf..'
254 shutil.copy('etc/indico.conf.sample', local)
256 for f in [x for x in ('etc/zdctl.conf', 'etc/zodb.conf', 'etc/logging.conf') if not os.path.exists(x)]:
257 shutil.copy('%s.sample' % f, f)
259 print """\nIndico needs to store some information in the filesystem (database, cache, temporary files, logs...)
260 Please specify the directory where you'd like it to be placed.
261 (Note that putting it outside of your sourcecode tree is recommended)"""
262 prefixDirDefault = os.path.dirname(os.getcwd())
263 prefixDir = raw_input('[%s]: ' % prefixDirDefault).strip()
265 if prefixDir == '':
266 prefixDir = prefixDirDefault
268 directories = dict((d, os.path.join(prefixDir, d)) for d in
269 ['db', 'log', 'tmp', 'cache', 'archive'])
271 print 'Creating directories...',
272 for d in directories.values():
273 if not os.path.exists(d):
274 os.makedirs(d)
275 print 'Done!'
277 directories['htdocs'] = os.path.join(os.getcwd(), 'indico', 'htdocs')
278 directories['bin'] = os.path.join(os.getcwd(), 'bin')
279 directories['etc'] = os.path.join(os.getcwd(), 'etc')
280 directories['doc'] = os.path.join(os.getcwd(), 'doc')
282 self._update_conf_dir_paths(local, directories)
284 directories.pop('htdocs') #avoid modifying the htdocs folder permissions (it brings problems with git)
286 from MaKaC.consoleScripts.installBase import _databaseText, _findApacheUserGroup, _checkDirPermissions, _updateDbConfigFiles, _updateMaKaCEggCache
288 user = ''
290 sourcePath = os.getcwd()
292 if sys.platform == "linux2":
293 # find the apache user/group
294 user, group = _findApacheUserGroup(None, None)
295 _checkDirPermissions(directories, dbInstalledBySetupPy = directories['db'], accessuser = user, accessgroup = group)
297 _updateDbConfigFiles(directories['db'], directories['log'], os.path.join(sourcePath, 'etc'), directories['tmp'], user)
299 _updateMaKaCEggCache(os.path.join(os.path.dirname(__file__), 'indico', 'MaKaC', '__init__.py'), directories['tmp'])
301 updateIndicoConfPathInsideMaKaCConfig(os.path.join(os.path.dirname(__file__), ''), 'indico/MaKaC/common/MaKaCConfig.py')
302 compileAllLanguages(self)
303 print '''
305 ''' % _databaseText('etc')
307 if sys.platform == "linux2":
308 # create symlink to legacy MaKaC dir
309 # this is so that the ".egg-link" created by the "develop" command works
310 os.symlink('indico/MaKaC','MaKaC')
312 def _update_conf_dir_paths(self, filePath, dirs):
313 fdata = open(filePath).read()
314 for dir in dirs.items():
315 d = dir[1].replace("\\","/") # For Windows users
316 fdata = re.sub('\/opt\/indico\/%s'%dir[0], d, fdata)
317 open(filePath, 'w').write(fdata)
319 class test_indico(Command):
321 Test command for Indico
324 description = "Test Suite Framework"
325 user_options = [('specify=', None, "Use nosetests style (file.class:testcase)"),
326 ('coverage', None, "Output coverage report in html"),
327 ('unit', None, "Run only Unit tests"),
328 ('functional', None, "Run only Functional tests"),
329 ('pylint', None, "Run python source analysis"),
330 ('jsunit', None, "Run js unit tests"),
331 ('jslint', None, "Run js source analysis"),
332 ('jscoverage', None, "Output coverage report in html for js"),
333 ('jsspecify=', None, "Use js-test-driver style (TestCaseName.testName)"),
334 ('log=', None, "Log to console, using specified level"),
335 ('grid', None, "Use Selenium Grid"),
336 ('xml', None, "XML output"),
337 ('html', None, "Make an HTML report (when possible)"),
338 ('record', None, "Record tests (for --functional)"),
339 ('parallel', None, "Parallel test execution using Selenium Grid (for --functional)"),
340 ('threads=', None, "Parallel test execution with several threads (for --functional)"),
341 ('repeat=', None, "Number of repetitions (for --functional)"),
342 ('silent', None, "Don't output anything in the console, just generate the report"),
343 ('killself', None, "Kill this script right after the tests finished without waiting for db shutdown.")]
344 boolean_options = []
346 specify = None
347 coverage = False
348 unit = False
349 functional = False
350 pylint = False
351 jsunit = False
352 jslint = False
353 jscoverage = False
354 jsspecify = None
355 grid = None
356 silent = False
357 killself = False
358 html = False
359 record = False
360 parallel = False
361 threads = False
362 repeat = False
363 log = False
364 xml = False
366 def initialize_options(self):
367 pass
369 def finalize_options(self):
370 pass
372 def run(self):
374 if not self.checkTestPackages():
375 print "Please install those missing packages before launching the tests again"
376 sys.exit(-1)
378 #missing jars will be downloaded automatically
379 if not self.checkTestJars():
380 print "Some jars could not be downloaded. Please download the missing jars manually"
381 sys.exit(-1)
383 from indico.tests import TestManager, TEST_RUNNERS
384 testsToRun = []
386 allTests = TEST_RUNNERS.keys()
388 for testType in allTests:
389 if getattr(self, testType):
390 testsToRun.append(testType)
392 if self.jsspecify and 'jsunit' not in testsToRun:
393 testsToRun.append('jsunit')
395 if testsToRun == []:
396 testsToRun = allTests
399 options = {'silent': self.silent,
400 'killself': self.killself,
401 'html': self.html,
402 'specify': self.specify,
403 'coverage': self.coverage,
404 'record': self.record,
405 'parallel': self.parallel,
406 'threads': self.threads,
407 'repeat': self.repeat,
408 'log': self.log,
409 'xml':self.xml}
411 # get only options that are active
412 options = dict((k,v) for (k,v) in options.iteritems() if v)
414 manager = TestManager()
416 result = manager.main(testsToRun, options)
418 sys.exit(result)
420 def checkTestPackages(self):
421 packagesList = ['figleaf',
422 'nose',
423 'rednose',
424 'selenium',
425 'twill']
426 validPackages = True
428 for package in packagesList:
429 try:
430 pkg_resources.require(package)
431 except pkg_resources.DistributionNotFound:
432 print """
433 %s not found! Please install it.
434 i.e. try 'easy_install %s'""" % (package, package)
435 validPackages = False
436 return validPackages
438 def checkTestJars(self):
440 check if needed jars are here, if not,
441 dowload them and unzip the file if necessary
444 from indico.tests import TestConfig
446 jarsList = {}
447 currentFilePath = os.path.dirname(__file__)
448 testModPath = os.path.join(currentFilePath, 'indico', 'tests')
450 try:
451 jarsList['jsunit'] = {'path': os.path.join(testModPath,
452 'javascript',
453 'unit'),
454 'url': TestConfig.getInstance().getJSUnitURL(),
455 'filename': TestConfig.getInstance().getJSUnitFilename()}
457 jarsList['jscoverage'] = {'path': os.path.join(testModPath,
458 'javascript',
459 'unit',
460 'plugins'),
461 'url': TestConfig.getInstance().getJSCoverageURL(),
462 'filename': TestConfig.getInstance().getJSCoverageFilename()}
464 jarsList['selenium'] = {'path': os.path.join(testModPath,
465 'python',
466 'functional'),
467 'url': TestConfig.getInstance().getSeleniumURL(),
468 'filename': TestConfig.getInstance().getSeleniumFilename()}
469 except KeyError, key:
470 print "[ERR] Please specify a value for %s in tests.conf" % key
471 sys.exit(1)
473 validJars = True
475 for name in jarsList:
476 jar = jarsList[name]
477 #check if jar is already here
478 if not os.path.exists(os.path.join(jar['path'], jar['filename'])):
479 print "Downloading %s to %s..." % (jar['url'], jar['path'])
480 try:
481 self.download(jar['url'], jar['path'])
482 except IOError, e:
483 validJars = validJars and False
484 print 'Could not download %s from %s (%s)' % (jar['filename'], jar['url'], e)
486 return validJars
488 def download(self, url, path):
489 """Copy the contents of a file from a given URL
490 to a local file.
492 import urllib
493 webFile = urllib.urlopen(url)
494 localFile = open(os.path.join(path, url.split('/')[-1]), 'w')
495 localFile.write(webFile.read())
496 webFile.close()
497 localFile.close()
499 def unzip(self, zipPath, inZipPath, targetFile):
500 """extract the needed file from zip and then delete the zip"""
501 import zipfile
502 try:
503 zfobj = zipfile.ZipFile(zipPath)
504 outfile = open(targetFile, 'wb')
505 outfile.write(zfobj.read(inZipPath))
506 outfile.flush()
507 outfile.close()
509 #delete zip file
510 os.unlink(zipPath)
511 except NameError, e:
512 print e
515 class egg_filename(Command):
516 description = "Get the file name of the generated egg"
517 user_options = []
518 boolean_options = []
520 def initialize_options(self):
521 pass
523 def finalize_options(self):
524 ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
525 self.egg_info = ei_cmd.egg_info
527 basename = pkg_resources.Distribution(
528 None, None, ei_cmd.egg_name, ei_cmd.egg_version,
529 get_python_version(),
530 self.distribution.has_ext_modules() and pkg_utils.get_build_platform
531 ).egg_name()
533 print basename
536 def run(self):
537 pass
540 if __name__ == '__main__':
541 # Always load source from the current folder
542 sys.path = [os.path.abspath('indico')] + sys.path
544 #PWD_INDICO_CONF = 'etc/indico.conf'
545 #if not os.path.exists(PWD_INDICO_CONF):
546 # shutil.copy('etc/indico.conf.sample', PWD_INDICO_CONF)
548 from MaKaC.consoleScripts.installBase import *
551 #Dirty trick: For running tests, we need to load all the modules and get rid of unnecessary outputs
552 tempLoggingDir = None
553 if 'test' in sys.argv:
554 import logging
555 import tempfile
556 tempLoggingDir = tempfile.mkdtemp()
557 logging.basicConfig(filename=os.path.join(tempLoggingDir, 'logging'),
558 level=logging.DEBUG)
559 setIndicoInstallMode(False)
560 else:
561 setIndicoInstallMode(True)
563 x = vars()
564 x.packageDir = os.path.join(get_python_lib(), 'MaKaC')
567 x.binDir = 'bin'
568 x.documentationDir = 'doc'
569 x.configurationDir = 'etc'
570 x.htdocsDir = 'htdocs'
572 dataFiles = _getDataFiles(x)
574 foundPackages = list('MaKaC.%s' % pkg for pkg in
575 find_packages(where = 'indico/MaKaC'))
576 foundPackages.append('MaKaC')
577 foundPackages.append('htdocs')
579 # add our namespace package
580 foundPackages += list('indico.%s' % pkg for pkg in
581 find_packages(where = 'indico',
582 exclude = ['htdocs*', 'MaKaC*']))
583 foundPackages.append('indico')
585 cmdclass = {'sdist': sdist_indico,
586 'bdist': _bdist_indico(dataFiles),
587 'bdist_egg': _bdist_egg_indico(dataFiles),
588 'jsbuild': jsbuild,
589 'fetchdeps': fetchdeps_indico,
590 'develop_config': develop_indico,
591 'test': test_indico,
592 'egg_filename': egg_filename
595 if BABEL_PRESENT:
596 for cmdname in ['init_catalog', 'extract_messages', 'compile_catalog', 'update_catalog']:
597 cmdclass['%s_js' % cmdname] = getattr(babel, cmdname)
598 cmdclass['compile_catalog_js'] = i18n.generate_messages_js
600 setup(name = "indico",
601 cmdclass = cmdclass,
602 version = _versionInit(),
603 description = "Indico is a full-featured conference lifecycle management and meeting/lecture scheduling tool",
604 author = "Indico Team",
605 author_email = "indico-team@cern.ch",
606 url = "http://indico-software.org",
607 download_url = "http://indico-software.org/wiki/Releases/Indico0.98-rc1",
608 platforms = ["any"],
609 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",
610 license = "http://www.gnu.org/licenses/gpl-2.0.txt",
611 entry_points = """
612 [console_scripts]
614 indico_scheduler = indico.modules.scheduler.daemon_script:main
615 indico_initial_setup = MaKaC.consoleScripts.indicoInitialSetup:main
616 indico_ctl = MaKaC.consoleScripts.indicoCtl:main
617 indico_livesync = indico.ext.livesync.console:main
618 indico_shell = indico.util.shell:main
620 [indico.ext_types]
622 Collaboration = MaKaC.plugins.Collaboration
623 InstantMessaging = MaKaC.plugins.InstantMessaging
624 RoomBooking = MaKaC.plugins.RoomBooking
625 EPayment = MaKaC.plugins.EPayment
626 livesync = indico.ext.livesync
627 importer = indico.ext.importer
630 [indico.ext]
632 Collaboration.EVO = MaKaC.plugins.Collaboration.EVO
633 Collaboration.Vidyo = MaKaC.plugins.Collaboration.Vidyo
634 Collaboration.CERNMCU = MaKaC.plugins.Collaboration.CERNMCU
635 Collaboration.RecordingManager = MaKaC.plugins.Collaboration.RecordingManager
636 Collaboration.RecordingRequest = MaKaC.plugins.Collaboration.RecordingRequest
637 Collaboration.WebcastRequest = MaKaC.plugins.Collaboration.WebcastRequest
639 RoomBooking.CERN = MaKaC.plugins.RoomBooking.CERN
640 RoomBooking.default = MaKaC.plugins.RoomBooking.default
642 EPayment.payPal = MaKaC.plugins.EPayment.payPal
643 EPayment.worldPay = MaKaC.plugins.EPayment.worldPay
644 EPayment.yellowPay = MaKaC.plugins.EPayment.yellowPay
645 EPayment.skipjack = MaKaC.plugins.EPayment.skipjack
647 importer.invenio = indico.ext.importer.invenio
648 importer.dummy = indico.ext.importer.dummy
650 InstantMessaging.XMPP = MaKaC.plugins.InstantMessaging.XMPP
652 livesync.invenio = indico.ext.livesync.invenio
653 livesync.cern_search = indico.ext.livesync.cern_search
655 """,
656 zip_safe = False,
657 packages = foundPackages,
658 package_dir = { 'indico': 'indico',
659 'htdocs': os.path.join('indico', 'htdocs'),
660 'MaKaC' : os.path.join('indico', 'MaKaC')},
661 package_data = {'indico': ['*.*']},
662 include_package_data=True,
663 namespace_packages = ['indico', 'indico.ext'],
664 install_requires = _getInstallRequires(),
665 data_files = dataFiles,
666 dependency_links = [
667 EXTRA_RESOURCES_URL
671 #delete the temp folder used for logging
672 if 'test' in sys.argv:
673 shutil.rmtree(tempLoggingDir)