[FIX] Remember advanced options search
[cds-indico.git] / setup.py
blob1054be3ee110f01895de818a2f7ca3417cfaddf3
1 # -*- coding: utf-8 -*-
2 ##
3 ##
4 ## This file is part of Indico.
5 ## Copyright (C) 2002 - 2012 European Organization for Nuclear Research (CERN)
6 ##
7 ## 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 3 of the
10 ## License, or (at your option) any later version.
12 ## 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 Indico;if not, see <http://www.gnu.org/licenses/>.
20 # Autoinstalls setuptools if the user doesn't have them already
21 import ez_setup
22 ez_setup.use_setuptools()
24 import commands
25 import getopt
26 import os
27 import re
28 import shutil
29 import string
30 import sys
31 import itertools
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, test
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 DEPENDENCY_URLS = ["http://indico-software.org/wiki/Admin/Installation/IndicoExtras",
51 "https://github.com/collective/icalendar/tarball/6f899869d462a23d0ebd3f54fb237e8670242bc4#egg=icalendar-3.0"]
53 DEVELOP_REQUIRES = ['pojson>=0.4', 'termcolor']
55 if sys.platform == 'linux2':
56 import pwd
57 import grp
60 class vars(object):
61 '''Variable holder.'''
62 packageDir = None
63 versionVal = 'None'
64 accessuser = None
65 accessgroup = None
66 dbInstalledBySetupPy = False
67 binDir = None
68 documentationDir = None
69 configurationDir = None
70 htdocsDir = None
72 ### Methods required by setup() ##############################################
74 def _generateDataPaths(x):
76 dataFilesDict = {}
78 for (baseDstDir, srcDir) in x:
79 for f in findall(srcDir):
80 dst_dir = os.path.join(baseDstDir,
81 os.path.relpath(os.path.dirname(f), srcDir))
82 if dst_dir not in dataFilesDict:
83 dataFilesDict[dst_dir] = []
84 dataFilesDict[dst_dir].append(f)
86 dataFiles = []
87 for k, v in dataFilesDict.items():
88 dataFiles.append((k, v))
90 return dataFiles
92 def _getDataFiles(x):
93 """
94 Returns a fully populated data_files ready to be fed to setup()
96 WARNING: when creating a bdist_egg we need to include files inside bin,
97 doc, config & htdocs into the egg therefore we cannot fetch indico.conf
98 values directly because they will not refer to the proper place. We
99 include those files in the egg's root folder.
102 # setup expects a list like this (('foo/bar/baz', 'wiki.py'),
103 # ('a/b/c', 'd.jpg'))
105 # What we do below is transform a list like this:
106 # (('foo', 'bar/baz/wiki.py'),
107 # ('a', 'b/c/d.jpg'))
109 # first into a dict and then into a pallatable form for setuptools.
111 # This re will be used to filter out etc/*.conf files and therefore not overwritting them
112 dataFiles = _generateDataPaths((('bin', 'bin'),
113 ('doc', 'doc'),
114 ('etc', 'etc')))
115 return dataFiles
118 def _getInstallRequires():
119 '''Returns external packages required by Indico
121 These are the ones needed for runtime.'''
123 base = ['ZODB3>=3.8', 'pytz', 'zope.index', 'zope.interface',
124 'lxml', 'cds-indico-extras', 'zc.queue', 'python-dateutil<2.0',
125 'pypdf', 'mako>=0.4.1', 'babel', 'icalendar>=3.0', 'pyatom',
126 'simplejson', 'python-cjson', 'webassets', 'jsmin', 'cssmin', 'pojson']
128 #for Python older than 2.7
129 if sys.version_info[0] <= 2 and sys.version_info[1] < 7:
130 base.append('argparse')
132 return base
135 def _versionInit():
136 '''Retrieves the version number from indico/MaKaC/__init__.py and returns it'''
138 from indico.MaKaC import __version__
139 v = __version__
141 print('Indico %s' % v)
143 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 sdist.sdist.run(self)
156 def _bdist_indico(dataFiles):
157 class bdist_indico(bdist.bdist):
158 def run(self):
159 compileAllLanguages(self)
160 bdist.bdist.run(self)
162 bdist_indico.dataFiles = dataFiles
163 return bdist_indico
166 def _bdist_egg_indico(dataFiles):
167 class bdist_egg_indico(bdist_egg.bdist_egg):
168 def run(self):
169 compileAllLanguages(self)
170 bdist_egg.bdist_egg.run(self)
172 bdist_egg_indico.dataFiles = dataFiles
173 return bdist_egg_indico
176 class develop_indico(develop.develop):
177 def run(self):
178 develop.develop.run(self)
180 # create symlink to legacy MaKaC dir
181 # this is so that the ".egg-link" created by the "develop" command works
182 if sys.platform in ["linux2", "darwin"] and not os.path.exists('MaKaC'):
183 os.symlink('indico/MaKaC','MaKaC')
186 class develop_config(develop_indico):
187 description = "prepares the current directory for Indico development"
188 user_options = develop.develop.user_options + [('www-uid=', None, "Set user for cache/log/db (typically apache user)"),
189 ('www-gid=', None, "Set group for cache/log/db (typically apache group)")]
191 www_uid = None
192 www_gid = None
194 def run(self):
195 # dependencies, links, etc...
196 develop_indico.run(self)
198 env = pkg_resources.Environment()
199 easy_install.main(DEVELOP_REQUIRES)
200 env.scan()
202 local = 'etc/indico.conf'
203 if os.path.exists(local):
204 print 'Upgrading existing etc/indico.conf..'
205 upgrade_indico_conf(local, 'etc/indico.conf.sample')
206 else:
207 print 'Creating new etc/indico.conf..'
208 shutil.copy('etc/indico.conf.sample', local)
210 for f in [x for x in ('etc/zdctl.conf', 'etc/zodb.conf', 'etc/logging.conf') if not os.path.exists(x)]:
211 shutil.copy('%s.sample' % f, f)
213 print """\nIndico needs to store some information in the filesystem (database, cache, temporary files, logs...)
214 Please specify the directory where you'd like it to be placed.
215 (Note that putting it outside of your sourcecode tree is recommended)"""
216 prefixDirDefault = os.path.dirname(os.getcwd())
217 prefixDir = raw_input('[%s]: ' % prefixDirDefault).strip()
219 if prefixDir == '':
220 prefixDir = prefixDirDefault
222 directories = dict((d, os.path.join(prefixDir, d)) for d in
223 ['db', 'log', 'tmp', 'cache', 'archive'])
225 print 'Creating directories...',
226 for d in directories.values():
227 if not os.path.exists(d):
228 os.makedirs(d)
229 print 'Done!'
231 directories['htdocs'] = os.path.join(os.getcwd(), 'indico', 'htdocs')
232 directories['bin'] = os.path.join(os.getcwd(), 'bin')
233 directories['etc'] = os.path.join(os.getcwd(), 'etc')
234 directories['doc'] = os.path.join(os.getcwd(), 'doc')
236 self._update_conf_dir_paths(local, directories)
238 directories.pop('htdocs') #avoid modifying the htdocs folder permissions (it brings problems with git)
240 from MaKaC.consoleScripts.installBase import _databaseText, _findApacheUserGroup, _checkDirPermissions, _updateDbConfigFiles, _updateMaKaCEggCache
242 user = ''
244 sourcePath = os.getcwd()
246 # find the apache user/group
247 user, group = _findApacheUserGroup(self.www_uid, self.www_gid)
248 _checkDirPermissions(directories, dbInstalledBySetupPy=directories['db'], accessuser=user, accessgroup=group)
250 _updateDbConfigFiles(directories['db'], directories['log'], os.path.join(sourcePath, 'etc'), directories['tmp'], user)
252 _updateMaKaCEggCache(os.path.join(os.path.dirname(__file__), 'indico', 'MaKaC', '__init__.py'), directories['tmp'])
254 updateIndicoConfPathInsideMaKaCConfig(os.path.join(os.path.dirname(__file__), ''), 'indico/MaKaC/common/MaKaCConfig.py')
255 compileAllLanguages(self)
256 print '''
258 ''' % _databaseText('etc')
260 def _update_conf_dir_paths(self, filePath, dirs):
261 fdata = open(filePath).read()
262 for dir in dirs.items():
263 d = dir[1].replace("\\","/") # For Windows users
264 fdata = re.sub('\/opt\/indico\/%s'%dir[0], d, fdata)
265 open(filePath, 'w').write(fdata)
267 class test_indico(test.test):
269 Test command for Indico
272 description = "Test Suite Framework"
273 user_options = test.test.user_options + [('specify=', None, "Use nosetests style (file.class:testcase)"),
274 ('coverage', None, "Output coverage report in html"),
275 ('unit', None, "Run only Unit tests"),
276 ('functional', None, "Run only Functional tests"),
277 ('pylint', None, "Run python source analysis"),
278 ('jsunit', None, "Run js unit tests"),
279 ('jslint', None, "Run js source analysis"),
280 ('jscoverage', None, "Output coverage report in html for js"),
281 ('jsspecify=', None, "Use js-test-driver style (TestCaseName.testName)"),
282 ('log=', None, "Log to console, using specified level"),
283 ('browser=', None, "Browser to use for functional tests"),
284 ('mode=', None, "Mode to use for functional tests"),
285 ('server-url=', None, "Server URL to use for functional tests"),
286 ('xml', None, "XML output"),
287 ('html', None, "Make an HTML report (when possible)"),
288 ('record', None, "Record tests (for --functional)"),
289 ('silent', None, "Don't output anything in the console, just generate the report"),
290 ('killself', None, "Kill this script right after the tests finished without waiting for db shutdown.")]
291 boolean_options = []
293 specify = None
294 coverage = False
295 unit = False
296 functional = False
297 browser = None
298 pylint = False
299 jsunit = False
300 jslint = False
301 jscoverage = False
302 jsspecify = None
303 silent = False
304 mode = None
305 server_url = None
306 killself = False
307 html = False
308 record = False
309 log = False
310 xml = False
312 def _wrap(self, func, *params):
313 def wrapped():
314 self.res = func(*params)
315 self.with_project_on_sys_path(wrapped)
316 return self.res
318 def finalize_options(self):
319 testsToRun = []
321 allTests = ['unit', 'functional']
323 for testType in allTests:
324 if getattr(self, testType):
325 testsToRun.append(testType)
327 if self.jsspecify and 'jsunit' not in testsToRun:
328 testsToRun.append('jsunit')
330 if testsToRun == []:
331 testsToRun = allTests
332 self.testsToRun = testsToRun
334 def run(self):
336 if self.distribution.install_requires:
337 self.distribution.fetch_build_eggs(self.distribution.install_requires)
338 if self.distribution.tests_require:
339 self.distribution.fetch_build_eggs(self.distribution.tests_require)
341 from indico.tests import TestManager
343 options = {'silent': self.silent,
344 'killself': self.killself,
345 'html': self.html,
346 'browser': self.browser,
347 'mode': self.mode,
348 'specify': self.specify,
349 'coverage': self.coverage,
350 'record': self.record,
351 'server_url': self.server_url,
352 'log': self.log,
353 'xml':self.xml}
355 # get only options that are active
356 options = dict((k,v) for (k,v) in options.iteritems() if v)
358 manager = TestManager()
359 result = self._wrap(manager.main, self.testsToRun, options)
361 sys.exit(result)
363 def download(self, url, path):
364 """Copy the contents of a file from a given URL
365 to a local file.
367 import urllib
368 webFile = urllib.urlopen(url)
369 localFile = open(os.path.join(path, url.split('/')[-1]), 'w')
370 localFile.write(webFile.read())
371 webFile.close()
372 localFile.close()
374 def unzip(self, zipPath, inZipPath, targetFile):
375 """extract the needed file from zip and then delete the zip"""
376 import zipfile
377 try:
378 zfobj = zipfile.ZipFile(zipPath)
379 outfile = open(targetFile, 'wb')
380 outfile.write(zfobj.read(inZipPath))
381 outfile.flush()
382 outfile.close()
384 #delete zip file
385 os.unlink(zipPath)
386 except NameError, e:
387 print e
390 class egg_filename(Command):
391 description = "Get the file name of the generated egg"
392 user_options = []
393 boolean_options = []
395 def initialize_options(self):
396 pass
398 def finalize_options(self):
399 ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
400 self.egg_info = ei_cmd.egg_info
402 basename = pkg_resources.Distribution(
403 None, None, ei_cmd.egg_name, ei_cmd.egg_version,
404 get_python_version(),
405 self.distribution.has_ext_modules() and pkg_utils.get_build_platform
406 ).egg_name()
408 print basename
411 def run(self):
412 pass
415 if __name__ == '__main__':
416 # Always load source from the current folder
417 sys.path = [os.path.abspath('indico')] + sys.path
419 #PWD_INDICO_CONF = 'etc/indico.conf'
420 #if not os.path.exists(PWD_INDICO_CONF):
421 # shutil.copy('etc/indico.conf.sample', PWD_INDICO_CONF)
423 from MaKaC.consoleScripts.installBase import *
426 #Dirty trick: For running tests, we need to load all the modules and get rid of unnecessary outputs
427 tempLoggingDir = None
428 if 'test' in sys.argv:
429 import logging
430 import tempfile
431 tempLoggingDir = tempfile.mkdtemp()
432 logging.basicConfig(filename=os.path.join(tempLoggingDir, 'logging'),
433 level=logging.DEBUG)
434 setIndicoInstallMode(False)
435 else:
436 setIndicoInstallMode(True)
438 x = vars()
439 x.packageDir = os.path.join(get_python_lib(), 'MaKaC')
442 x.binDir = 'bin'
443 x.documentationDir = 'doc'
444 x.configurationDir = 'etc'
445 x.htdocsDir = 'htdocs'
447 dataFiles = _getDataFiles(x)
449 foundPackages = list('MaKaC.%s' % pkg for pkg in
450 find_packages(where = 'indico/MaKaC'))
451 foundPackages.append('MaKaC')
452 foundPackages.append('htdocs')
454 # add our namespace package
455 foundPackages += list('indico.%s' % pkg for pkg in
456 find_packages(where = 'indico',
457 exclude = ['htdocs*', 'MaKaC*']))
458 foundPackages.append('indico')
460 cmdclass = {'sdist': sdist_indico,
461 'bdist': _bdist_indico(dataFiles),
462 'bdist_egg': _bdist_egg_indico(dataFiles),
463 'develop_config': develop_config,
464 'develop': develop_indico,
465 'test': test_indico,
466 'egg_filename': egg_filename
469 if BABEL_PRESENT:
470 for cmdname in ['init_catalog', 'extract_messages', 'compile_catalog', 'update_catalog']:
471 cmdclass['%s_js' % cmdname] = getattr(babel, cmdname)
472 cmdclass['compile_catalog_js'] = i18n.generate_messages_js
474 setup(name = "indico",
475 cmdclass = cmdclass,
476 version = _versionInit(),
477 description = "Indico is a full-featured conference lifecycle management and meeting/lecture scheduling tool",
478 author = "Indico Team",
479 author_email = "indico-team@cern.ch",
480 url = "http://indico-software.org",
481 download_url = "http://indico-software.org/wiki/Releases/Indico0.98-rc1",
482 platforms = ["any"],
483 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",
484 license = "http://www.gnu.org/licenses/gpl-2.0.txt",
485 entry_points = """
486 [console_scripts]
488 indico_scheduler = indico.modules.scheduler.daemon_script:main
489 indico_initial_setup = MaKaC.consoleScripts.indicoInitialSetup:main
490 indico_ctl = MaKaC.consoleScripts.indicoCtl:main
491 indico_livesync = indico.ext.livesync.console:main
492 indico_shell = indico.util.shell:main
494 [indico.ext_types]
496 statistics = indico.ext.statistics
497 Collaboration = MaKaC.plugins.Collaboration
498 InstantMessaging = MaKaC.plugins.InstantMessaging
499 RoomBooking = MaKaC.plugins.RoomBooking
500 EPayment = MaKaC.plugins.EPayment
501 livesync = indico.ext.livesync
502 importer = indico.ext.importer
504 [indico.ext]
506 statistics.piwik = indico.ext.statistics.piwik
508 Collaboration.EVO = MaKaC.plugins.Collaboration.EVO
509 Collaboration.Vidyo = MaKaC.plugins.Collaboration.Vidyo
510 Collaboration.CERNMCU = MaKaC.plugins.Collaboration.CERNMCU
511 Collaboration.RecordingManager = MaKaC.plugins.Collaboration.RecordingManager
512 Collaboration.RecordingRequest = MaKaC.plugins.Collaboration.RecordingRequest
513 Collaboration.WebcastRequest = MaKaC.plugins.Collaboration.WebcastRequest
515 RoomBooking.CERN = MaKaC.plugins.RoomBooking.CERN
516 RoomBooking.default = MaKaC.plugins.RoomBooking.default
518 EPayment.payPal = MaKaC.plugins.EPayment.payPal
519 EPayment.worldPay = MaKaC.plugins.EPayment.worldPay
520 EPayment.yellowPay = MaKaC.plugins.EPayment.yellowPay
521 EPayment.skipjack = MaKaC.plugins.EPayment.skipjack
523 importer.invenio = indico.ext.importer.invenio
524 importer.dummy = indico.ext.importer.dummy
526 InstantMessaging.XMPP = MaKaC.plugins.InstantMessaging.XMPP
528 livesync.invenio = indico.ext.livesync.invenio
529 livesync.cern_search = indico.ext.livesync.cern_search
531 """,
532 zip_safe = False,
533 packages = foundPackages,
534 package_dir = { 'indico': 'indico',
535 'htdocs': os.path.join('indico', 'htdocs'),
536 'MaKaC' : os.path.join('indico', 'MaKaC')},
537 package_data = {'indico': ['*.*']},
538 include_package_data=True,
539 namespace_packages = ['indico', 'indico.ext'],
540 install_requires = _getInstallRequires(),
541 tests_require = ['nose', 'rednose', 'twill', 'selenium', 'figleaf'],
542 data_files = dataFiles,
543 dependency_links = DEPENDENCY_URLS
546 #delete the temp folder used for logging
547 if 'test' in sys.argv:
548 shutil.rmtree(tempLoggingDir)