[FIX] qTip 2 updated version
[cds-indico.git] / setup.py
blob2da814c40fc77df7f300e0abcd1f05a878703892
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://cdswaredev.cern.ch/indico/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, files, remove_first_x_chars) in x:
76 for f in files:
77 dst_dir = os.path.join(baseDstDir, os.path.dirname(f)[remove_first_x_chars:])
78 if dst_dir not in dataFilesDict:
79 dataFilesDict[dst_dir] = []
80 dataFilesDict[dst_dir].append(f)
82 dataFiles = []
83 for k, v in dataFilesDict.items():
84 dataFiles.append((k, v))
86 return dataFiles
88 def _getDataFiles(x):
89 """
90 Returns a fully populated data_files ready to be fed to setup()
92 WARNING: when creating a bdist_egg we need to include files inside bin,
93 doc, config & htdocs into the egg therefore we cannot fetch indico.conf
94 values directly because they will not refer to the proper place. We
95 include those files in the egg's root folder.
96 """
98 # setup expects a list like this (('foo/bar/baz', 'wiki.py'),
99 # ('a/b/c', 'd.jpg'))
101 # What we do below is transform a list like this:
102 # (('foo', 'bar/baz/wiki.py'),
103 # ('a', 'b/c/d.jpg'))
105 # first into a dict and then into a pallatable form for setuptools.
107 # This re will be used to filter out etc/*.conf files and therefore not overwritting them
108 isAConfRe = re.compile('etc\/[^/]+\.conf$')
110 dataFiles = _generateDataPaths((('bin', findall('bin'), 4),
111 ('doc', findall('doc'), 4),
112 ('etc', [xx for xx in findall('etc') if not isAConfRe.search(xx)], 4),
113 ('htdocs', findall('indico/htdocs'), 14)))
114 return dataFiles
120 def _getInstallRequires():
121 '''Returns external packages required by Indico
123 These are the ones needed for runtime.'''
125 base = ['ZODB3>=3.8', 'pytz', 'zope.index', 'zope.interface',
126 'lxml', 'cds-indico-extras', 'zc.queue', 'python-dateutil<2.0',
127 'pypdf', 'mako>=0.4.1', 'babel']
129 #for Python older than 2.7
130 if sys.version_info[0] <= 2 and sys.version_info[1] < 7:
131 base.append('argparse')
133 return base
136 def _versionInit():
137 '''Retrieves the version number from indico/MaKaC/__init__.py and returns it'''
139 from indico.MaKaC import __version__
140 v = __version__
142 print('Indico %s' % v)
144 return v
146 ### Commands ###########################################################
147 class sdist_indico(sdist.sdist):
148 user_options = sdist.sdist.user_options + \
149 [('version=', None, 'version to distribute')]
150 version = 'dev'
152 def run(self):
153 global x
154 sdist.sdist.run(self)
157 class jsdist_indico:
158 def jsCompress(self):
159 from MaKaC.consoleScripts.installBase import jsCompress
160 jsCompress()
161 self.dataFiles += _generateDataPaths([
162 ('htdocs/js/presentation/pack', findall('indico/htdocs/js/presentation/pack'), 35),
163 ('htdocs/js/indico/pack', findall('indico/htdocs/js/indico/pack'), 29),
164 ('htdocs/js/livesync/pack', findall('indico/htdocs/js/indico/pack'), 31),
165 ('htdocs/js/jquery/pack', findall('indico/htdocs/js/indico/pack'), 29)])
168 def _bdist_indico(dataFiles):
169 class bdist_indico(bdist.bdist, jsdist_indico):
170 def run(self):
171 self.jsCompress()
172 compileAllLanguages(self)
173 bdist.bdist.run(self)
175 bdist_indico.dataFiles = dataFiles
176 return bdist_indico
179 def _bdist_egg_indico(dataFiles):
180 class bdist_egg_indico(bdist_egg.bdist_egg, jsdist_indico):
181 def run(self):
182 self.jsCompress()
183 compileAllLanguages(self)
184 bdist_egg.bdist_egg.run(self)
186 bdist_egg_indico.dataFiles = dataFiles
187 return bdist_egg_indico
190 class jsbuild(Command):
191 description = "minifies and packs javascript files"
192 user_options = []
193 boolean_options = []
195 def initialize_options(self):
196 pass
198 def finalize_options(self):
199 pass
201 def run(self):
202 from MaKaC.consoleScripts.installBase import jsCompress
203 jsCompress()
205 class fetchdeps:
206 def run(self):
207 print "Checking if dependencies need to be installed..."
209 wset = pkg_resources.working_set
211 wset.resolve(map(pkg_resources.Requirement.parse,
212 filter(lambda x: x != None, _getInstallRequires())),
213 installer=self._installMissing)
215 print "Done!"
218 def _installMissing(self, dist):
219 env = pkg_resources.Environment()
220 print dist, EXTRA_RESOURCES_URL
221 easy_install.main(["-f", EXTRA_RESOURCES_URL, "-U", str(dist)])
222 env.scan()
224 if env[str(dist)]:
225 return env[str(dist)][0]
226 else:
227 return None
230 class fetchdeps_indico(fetchdeps, Command):
231 description = "fetch all the dependencies needed to run Indico"
232 user_options = []
233 boolean_options = []
235 def initialize_options(self):
236 pass
238 def finalize_options(self):
239 pass
242 class develop_indico(Command):
243 description = "prepares the current directory for Indico development"
244 user_options = []
245 boolean_options = []
247 def initialize_options(self):
248 pass
250 def finalize_options(self):
251 pass
253 def run(self):
255 fetchdeps().run()
257 local = 'etc/indico.conf'
258 if os.path.exists(local):
259 print 'Upgrading existing etc/indico.conf..'
260 upgrade_indico_conf(local, 'etc/indico.conf.sample')
261 else:
262 print 'Creating new etc/indico.conf..'
263 shutil.copy('etc/indico.conf.sample', local)
265 for f in [x for x in ('etc/zdctl.conf', 'etc/zodb.conf', 'etc/logging.conf') if not os.path.exists(x)]:
266 shutil.copy('%s.sample' % f, f)
268 print """\nIndico needs to store some information in the filesystem (database, cache, temporary files, logs...)
269 Please specify the directory where you'd like it to be placed.
270 (Note that putting it outside of your sourcecode tree is recommended)"""
271 prefixDir = raw_input('[%s]: ' % os.getcwd()).strip()
273 if prefixDir == '':
274 prefixDir = os.getcwd()
276 directories = dict((d, os.path.join(prefixDir, d)) for d in
277 ['db', 'log', 'tmp', 'cache', 'archive'])
279 print 'Creating directories...',
280 for d in directories.values():
281 if not os.path.exists(d):
282 os.makedirs(d)
283 print 'Done!'
285 directories['htdocs'] = os.path.join(os.getcwd(), 'indico', 'htdocs')
286 directories['bin'] = os.path.join(os.getcwd(), 'bin')
287 directories['etc'] = os.path.join(os.getcwd(), 'etc')
288 directories['doc'] = os.path.join(os.getcwd(), 'doc')
290 self._update_conf_dir_paths(local, directories)
292 directories.pop('htdocs') #avoid modifying the htdocs folder permissions (it brings problems with git)
294 from MaKaC.consoleScripts.installBase import _databaseText, _findApacheUserGroup, _checkDirPermissions, _updateDbConfigFiles, _updateMaKaCEggCache
296 user = ''
298 sourcePath = os.getcwd()
300 if sys.platform == "linux2":
301 # find the apache user/group
302 user, group = _findApacheUserGroup(None, None)
303 _checkDirPermissions(directories, dbInstalledBySetupPy = directories['db'], accessuser = user, accessgroup = group)
305 _updateDbConfigFiles(directories['db'], directories['log'], os.path.join(sourcePath, 'etc'), directories['tmp'], user)
307 _updateMaKaCEggCache(os.path.join(os.path.dirname(__file__), 'indico', 'MaKaC', '__init__.py'), directories['tmp'])
309 updateIndicoConfPathInsideMaKaCConfig(os.path.join(os.path.dirname(__file__), ''), 'indico/MaKaC/common/MaKaCConfig.py')
310 compileAllLanguages(self)
311 print '''
313 ''' % _databaseText('etc')
315 if sys.platform == "linux2":
316 # create symlink to legacy MaKaC dir
317 # this is so that the ".egg-link" created by the "develop" command works
318 os.symlink('indico/MaKaC','MaKaC')
320 def _update_conf_dir_paths(self, filePath, dirs):
321 fdata = open(filePath).read()
322 for dir in dirs.items():
323 d = dir[1].replace("\\","/") # For Windows users
324 fdata = re.sub('\/opt\/indico\/%s'%dir[0], d, fdata)
325 open(filePath, 'w').write(fdata)
327 class test_indico(Command):
329 Test command for Indico
332 description = "Test Suite Framework"
333 user_options = [('specify=', None, "Use nosetests style (file.class:testcase)"),
334 ('coverage', None, "Output coverage report in html"),
335 ('unit', None, "Run only Unit tests"),
336 ('functional', None, "Run only Functional tests"),
337 ('pylint', None, "Run python source analysis"),
338 ('jsunit', None, "Run js unit tests"),
339 ('jslint', None, "Run js source analysis"),
340 ('jscoverage', None, "Output coverage report in html for js"),
341 ('jsspecify=', None, "Use js-test-driver style (TestCaseName.testName)"),
342 ('log=', None, "Log to console, using specified level"),
343 ('grid', None, "Use Selenium Grid"),
344 ('xml', None, "XML output"),
345 ('html', None, "Make an HTML report (when possible)"),
346 ('record', None, "Record tests (for --functional)"),
347 ('parallel', None, "Parallel test execution using Selenium Grid (for --functional)"),
348 ('threads=', None, "Parallel test execution with several threads (for --functional)"),
349 ('repeat=', None, "Number of repetitions (for --functional)"),
350 ('silent', None, "Don't output anything in the console, just generate the report")]
351 boolean_options = []
353 specify = None
354 coverage = False
355 unit = False
356 functional = False
357 pylint = False
358 jsunit = False
359 jslint = False
360 jscoverage = False
361 jsspecify = None
362 grid = None
363 silent = False
364 html = False
365 record = False
366 parallel = False
367 threads = False
368 repeat = False
369 log = False
370 xml = False
372 def initialize_options(self):
373 pass
375 def finalize_options(self):
376 pass
378 def run(self):
380 if not self.checkTestPackages():
381 print "Please install those missing packages before launching the tests again"
382 sys.exit(-1)
384 #missing jars will be downloaded automatically
385 if not self.checkTestJars():
386 print "Some jars could not be downloaded. Please download the missing jars manually"
387 sys.exit(-1)
389 from indico.tests import TestManager, TEST_RUNNERS
390 testsToRun = []
392 allTests = TEST_RUNNERS.keys()
394 for testType in allTests:
395 if getattr(self, testType):
396 testsToRun.append(testType)
398 if self.jsspecify and 'jsunit' not in testsToRun:
399 testsToRun.append('jsunit')
401 if testsToRun == []:
402 testsToRun = allTests
405 options = {'silent': self.silent,
406 'html': self.html,
407 'specify': self.specify,
408 'coverage': self.coverage,
409 'record': self.record,
410 'parallel': self.parallel,
411 'threads': self.threads,
412 'repeat': self.repeat,
413 'log': self.log,
414 'xml':self.xml}
416 # get only options that are active
417 options = dict((k,v) for (k,v) in options.iteritems() if v)
419 manager = TestManager()
421 result = manager.main(testsToRun, options)
423 sys.exit(result)
425 def checkTestPackages(self):
426 packagesList = ['figleaf',
427 'nose',
428 'rednose',
429 'selenium',
430 'twill']
431 validPackages = True
433 for package in packagesList:
434 try:
435 pkg_resources.require(package)
436 except pkg_resources.DistributionNotFound:
437 print """
438 %s not found! Please install it.
439 i.e. try 'easy_install %s'""" % (package, package)
440 validPackages = False
441 return validPackages
443 def checkTestJars(self):
445 check if needed jars are here, if not,
446 dowload them and unzip the file if necessary
449 from indico.tests import TestConfig
451 jarsList = {}
452 currentFilePath = os.path.dirname(__file__)
453 testModPath = os.path.join(currentFilePath, 'indico', 'tests')
455 try:
456 jarsList['jsunit'] = {'path': os.path.join(testModPath,
457 'javascript',
458 'unit'),
459 'url': TestConfig.getInstance().getJSUnitURL(),
460 'filename': TestConfig.getInstance().getJSUnitFilename()}
462 jarsList['jscoverage'] = {'path': os.path.join(testModPath,
463 'javascript',
464 'unit',
465 'plugins'),
466 'url': TestConfig.getInstance().getJSCoverageURL(),
467 'filename': TestConfig.getInstance().getJSCoverageFilename()}
469 jarsList['selenium'] = {'path': os.path.join(testModPath,
470 'python',
471 'functional'),
472 'url': TestConfig.getInstance().getSeleniumURL(),
473 'inZipPath': TestConfig.getInstance().getSeleniumInZipPath(),
474 'zipname': TestConfig.getInstance().getSeleniumZipname(),
475 'filename': TestConfig.getInstance().getSeleniumFilename()}
476 except KeyError, key:
477 print "[ERR] Please specify a value for %s in tests.conf" % key
478 sys.exit(1)
480 validJars = True
482 for name in jarsList:
483 jar = jarsList[name]
484 #check if jar is already here
485 if not os.path.exists(os.path.join(jar['path'], jar['filename'])):
486 print "Downloading %s to %s..." % (jar['url'], jar['path'])
487 try:
488 self.download(jar['url'], jar['path'])
490 #if a zipname is specified, we will unzip the target file pointed by inZipPath variable
491 try:
492 if jar['zipname'] != None:
493 self.unzip(os.path.join(jar['path'], jar['zipname']), jar['inZipPath'], os.path.join(jar['path'], jar['filename']))
494 except KeyError:
495 pass
497 except IOError, e:
498 validJars = validJars and False
499 print 'Could not download %s from %s (%s)' % (jar['filename'], jar['url'], e)
501 return validJars
503 def download(self, url, path):
504 """Copy the contents of a file from a given URL
505 to a local file.
507 import urllib
508 webFile = urllib.urlopen(url)
509 localFile = open(os.path.join(path, url.split('/')[-1]), 'w')
510 localFile.write(webFile.read())
511 webFile.close()
512 localFile.close()
514 def unzip(self, zipPath, inZipPath, targetFile):
515 """extract the needed file from zip and then delete the zip"""
516 import zipfile
517 try:
518 zfobj = zipfile.ZipFile(zipPath)
519 outfile = open(targetFile, 'wb')
520 outfile.write(zfobj.read(inZipPath))
521 outfile.flush()
522 outfile.close()
524 #delete zip file
525 os.unlink(zipPath)
526 except NameError, e:
527 print e
530 class egg_filename(Command):
531 description = "Get the file name of the generated egg"
532 user_options = []
533 boolean_options = []
535 def initialize_options(self):
536 pass
538 def finalize_options(self):
539 ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
540 self.egg_info = ei_cmd.egg_info
542 basename = pkg_resources.Distribution(
543 None, None, ei_cmd.egg_name, ei_cmd.egg_version,
544 get_python_version(),
545 self.distribution.has_ext_modules() and pkg_utils.get_build_platform
546 ).egg_name()
548 print basename
551 def run(self):
552 pass
555 if __name__ == '__main__':
556 # Always load source from the current folder
557 sys.path = [os.path.abspath('indico')] + sys.path
559 #PWD_INDICO_CONF = 'etc/indico.conf'
560 #if not os.path.exists(PWD_INDICO_CONF):
561 # shutil.copy('etc/indico.conf.sample', PWD_INDICO_CONF)
563 from MaKaC.consoleScripts.installBase import *
566 #Dirty trick: For running tests, we need to load all the modules and get rid of unnecessary outputs
567 tempLoggingDir = None
568 if 'test' in sys.argv:
569 import logging
570 import tempfile
571 tempLoggingDir = tempfile.mkdtemp()
572 logging.basicConfig(filename=os.path.join(tempLoggingDir, 'logging'),
573 level=logging.DEBUG)
574 setIndicoInstallMode(False)
575 else:
576 setIndicoInstallMode(True)
578 x = vars()
579 x.packageDir = os.path.join(get_python_lib(), 'MaKaC')
582 x.binDir = 'bin'
583 x.documentationDir = 'doc'
584 x.configurationDir = 'etc'
585 x.htdocsDir = 'htdocs'
587 dataFiles = _getDataFiles(x)
589 foundPackages = find_packages(where = 'indico',
590 exclude = ('htdocs*', 'tests*', 'core*', 'ext*',
591 'modules*', 'util*', 'web*', 'locale'))
593 # add our namespace package
594 foundPackages += list('indico.%s' % pkg for pkg in
595 find_packages(where = 'indico',
596 exclude = ('htdocs*','MaKaC*')))
598 foundPackages.append('indico')
600 cmdclass = {'sdist': sdist_indico,
601 'bdist': _bdist_indico(dataFiles),
602 'bdist_egg': _bdist_egg_indico(dataFiles),
603 'jsbuild': jsbuild,
604 'fetchdeps': fetchdeps_indico,
605 'develop_config': develop_indico,
606 'test': test_indico,
607 'egg_filename': egg_filename
610 if BABEL_PRESENT:
611 for cmdname in ['init_catalog', 'extract_messages', 'compile_catalog', 'update_catalog']:
612 cmdclass['%s_js' % cmdname] = getattr(babel, cmdname)
613 cmdclass['compile_catalog_js'] = i18n.generate_messages_js
615 setup(name = "indico",
616 cmdclass = cmdclass,
617 version = _versionInit(),
618 description = "Indico is a full-featured conference lifecycle management and meeting/lecture scheduling tool",
619 author = "Indico Team",
620 author_email = "indico-team@cern.ch",
621 url = "http://cdswaredev.cern.ch/indico",
622 download_url = "http://cdswaredev.cern.ch/indico/wiki/Releases/Indico0.97.0",
623 platforms = ["any"],
624 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",
625 license = "http://www.gnu.org/licenses/gpl-2.0.txt",
626 package_dir = { 'indico': 'indico',
627 'MaKaC' : os.path.join('indico', 'MaKaC')},
628 entry_points = """
629 [console_scripts]
631 indico_scheduler = indico.modules.scheduler.daemon_script:main
632 indico_initial_setup = MaKaC.consoleScripts.indicoInitialSetup:main
633 indico_ctl = MaKaC.consoleScripts.indicoCtl:main
634 indico_livesync = indico.ext.livesync.console:main
635 indico_shell = indico.util.shell:main
637 [indico.ext_types]
639 Collaboration = MaKaC.plugins.Collaboration
640 InstantMessaging = MaKaC.plugins.InstantMessaging
641 RoomBooking = MaKaC.plugins.RoomBooking
642 EPayment = MaKaC.plugins.EPayment
643 livesync = indico.ext.livesync
644 importer = indico.ext.importer
647 [indico.ext]
649 Collaboration.EVO = MaKaC.plugins.Collaboration.EVO
650 Collaboration.Vidyo = MaKaC.plugins.Collaboration.Vidyo
651 Collaboration.CERNMCU = MaKaC.plugins.Collaboration.CERNMCU
652 Collaboration.RecordingManager = MaKaC.plugins.Collaboration.RecordingManager
653 Collaboration.RecordingRequest = MaKaC.plugins.Collaboration.RecordingRequest
654 Collaboration.WebcastRequest = MaKaC.plugins.Collaboration.WebcastRequest
656 RoomBooking.CERN = MaKaC.plugins.RoomBooking.CERN
657 RoomBooking.default = MaKaC.plugins.RoomBooking.default
659 EPayment.payPal = MaKaC.plugins.EPayment.payPal
660 EPayment.worldPay = MaKaC.plugins.EPayment.worldPay
661 EPayment.yellowPay = MaKaC.plugins.EPayment.yellowPay
662 EPayment.skipjack = MaKaC.plugins.EPayment.skipjack
664 importer.invenio = indico.ext.importer.invenio
665 importer.dummy = indico.ext.importer.dummy
667 InstantMessaging.XMPP = MaKaC.plugins.InstantMessaging.XMPP
669 livesync.invenio = indico.ext.livesync.invenio
670 livesync.cern_search = indico.ext.livesync.cern_search
672 """,
673 zip_safe = False,
674 packages = foundPackages,
675 namespace_packages = ['indico'],
676 install_requires = _getInstallRequires(),
677 data_files = dataFiles,
678 package_data = {'indico': ['*.*'] },
679 include_package_data = True,
680 dependency_links = [
681 EXTRA_RESOURCES_URL
685 #delete the temp folder used for logging
686 if 'test' in sys.argv:
687 shutil.rmtree(tempLoggingDir)