]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
Update the dependency on Twisted to >= 10.1. This allows us to simplify some document...
[tahoe-lafs/tahoe-lafs.git] / setup.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # Tahoe-LAFS -- secure, distributed storage grid
5 #
6 # Copyright © 2008-2011 Allmydata, Inc.
7 #
8 # This file is part of Tahoe-LAFS.
9 #
10 # See the docs/about.rst file for licensing information.
11
12 import glob, os, stat, subprocess, sys, re
13
14 ##### sys.path management
15
16 def pylibdir(prefixdir):
17     pyver = "python%d.%d" % (sys.version_info[:2])
18     if sys.platform == "win32":
19         return os.path.join(prefixdir, "Lib", "site-packages")
20     else:
21         return os.path.join(prefixdir, "lib", pyver, "site-packages")
22
23 basedir = os.path.dirname(os.path.abspath(__file__))
24 supportlib = pylibdir(os.path.join(basedir, "support"))
25
26 # locate our version number
27
28 def read_version_py(infname):
29     try:
30         verstrline = open(infname, "rt").read()
31     except EnvironmentError:
32         return None
33     else:
34         VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
35         mo = re.search(VSRE, verstrline, re.M)
36         if mo:
37             return mo.group(1)
38
39 version = read_version_py("src/allmydata/_version.py")
40
41 APPNAME='allmydata-tahoe'
42 APPNAMEFILE = os.path.join('src', 'allmydata', '_appname.py')
43 APPNAMEFILESTR = "__appname__ = '%s'" % (APPNAME,)
44 try:
45     curappnamefilestr = open(APPNAMEFILE, 'rU').read()
46 except EnvironmentError:
47     # No file, or unreadable or something, okay then let's try to write one.
48     open(APPNAMEFILE, "w").write(APPNAMEFILESTR)
49 else:
50     if curappnamefilestr.strip() != APPNAMEFILESTR:
51         print "Error -- this setup.py file is configured with the 'application name' to be '%s', but there is already a file in place in '%s' which contains the contents '%s'.  If the file is wrong, please remove it and setup.py will regenerate it and write '%s' into it." % (APPNAME, APPNAMEFILE, curappnamefilestr, APPNAMEFILESTR)
52         sys.exit(-1)
53
54 # setuptools/zetuptoolz looks in __main__.__requires__ for a list of
55 # requirements. When running "python setup.py test", __main__ is
56 # setup.py, so we put the list here so that the requirements will be
57 # available for tests:
58
59 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
60 # the _auto_deps.install_requires list, which is used in the call to setup()
61 # below.
62 adglobals = {}
63 execfile('src/allmydata/_auto_deps.py', adglobals)
64 install_requires = adglobals['install_requires']
65
66 if len(sys.argv) > 1 and sys.argv[1] == '--fakedependency':
67     del sys.argv[1]
68     install_requires += ["fakedependency >= 1.0.0"]
69
70 __requires__ = install_requires[:]
71
72 egg = os.path.realpath(glob.glob('setuptools-*.egg')[0])
73 sys.path.insert(0, egg)
74 egg = os.path.realpath(glob.glob('darcsver-*.egg')[0])
75 sys.path.insert(0, egg)
76 egg = os.path.realpath(glob.glob('setuptools_darcs-*.egg')[0])
77 sys.path.insert(0, egg)
78 import setuptools; setuptools.bootstrap_install_from = egg
79
80 from setuptools import find_packages, setup
81 from setuptools.command import sdist
82 from setuptools import Command
83
84 trove_classifiers=[
85     "Development Status :: 5 - Production/Stable",
86     "Environment :: Console",
87     "Environment :: Web Environment",
88     "License :: OSI Approved :: GNU General Public License (GPL)",
89     "License :: DFSG approved",
90     "License :: Other/Proprietary License",
91     "Intended Audience :: Developers",
92     "Intended Audience :: End Users/Desktop",
93     "Intended Audience :: System Administrators",
94     "Operating System :: Microsoft",
95     "Operating System :: Microsoft :: Windows",
96     "Operating System :: Microsoft :: Windows :: Windows NT/2000",
97     "Operating System :: Unix",
98     "Operating System :: POSIX :: Linux",
99     "Operating System :: POSIX",
100     "Operating System :: MacOS :: MacOS X",
101     "Operating System :: OS Independent",
102     "Natural Language :: English",
103     "Programming Language :: C",
104     "Programming Language :: Python",
105     "Programming Language :: Python :: 2",
106     "Programming Language :: Python :: 2.4",
107     "Programming Language :: Python :: 2.5",
108     "Programming Language :: Python :: 2.6",
109     "Programming Language :: Python :: 2.7",
110     "Topic :: Utilities",
111     "Topic :: System :: Systems Administration",
112     "Topic :: System :: Filesystems",
113     "Topic :: System :: Distributed Computing",
114     "Topic :: Software Development :: Libraries",
115     "Topic :: Communications :: Usenet News",
116     "Topic :: System :: Archiving :: Backup",
117     "Topic :: System :: Archiving :: Mirroring",
118     "Topic :: System :: Archiving",
119     ]
120
121
122 setup_requires = []
123
124 # The darcsver command from the darcsver plugin is needed to initialize the
125 # distribution's .version attribute correctly. (It does this either by
126 # examining darcs history, or if that fails by reading the
127 # src/allmydata/_version.py file). darcsver will also write a new version
128 # stamp in src/allmydata/_version.py, with a version number derived from
129 # darcs history. Note that the setup.cfg file has an "[aliases]" section
130 # which enumerates commands that you might run and specifies that it will run
131 # darcsver before each one. If you add different commands (or if I forgot
132 # some that are already in use), you may need to add it to setup.cfg and
133 # configure it to run darcsver before your command, if you want the version
134 # number to be correct when that command runs.
135 # http://pypi.python.org/pypi/darcsver
136 setup_requires.append('darcsver >= 1.7.2')
137
138 # Nevow requires Twisted to setup, but prior to Nevow v0.9.33, didn't
139 # declare that requirement in a way that enables setuptools to satisfy
140 # the requirement before Nevow's setup.py tries to "import twisted".
141 # This only matters when Twisted is not already installed.
142 # See http://divmod.org/trac/ticket/2629
143 # Retire this hack when
144 # https://bugs.launchpad.net/nevow/+bug/812537 has been fixed.
145 setup_requires += [req for req in install_requires if req.startswith('Twisted')]
146
147 # setuptools_darcs is required to produce complete distributions (such
148 # as with "sdist" or "bdist_egg"), unless there is a
149 # src/allmydata_tahoe.egg-info/SOURCE.txt file present which contains
150 # a complete list of files that should be included.
151
152 # http://pypi.python.org/pypi/setuptools_darcs
153
154 # However, requiring it runs afoul of a bug in Distribute, which was
155 # shipped in Ubuntu Lucid, so for now you have to manually install it
156 # before building sdists or eggs:
157 # http://bitbucket.org/tarek/distribute/issue/55/revision-control-plugin-automatically-installed-as-a-build-dependency-is-not-present-when-another-build-dependency-is-being
158
159 # Note that we explicitly inject setuptools_darcs at the beginning of
160 # this setup.py file, so it is still in effect when building dists
161 # using this setup.py file even when the following requirement is
162 # disabled.
163 if False:
164     setup_requires.append('setuptools_darcs >= 1.1.0')
165
166 # trialcoverage is required if you want the "trial" unit test runner to have a
167 # "--reporter=bwverbose-coverage" option which produces code-coverage results.
168 # The required version is 0.3.3, because that is the latest version that only
169 # depends on a version of pycoverage for which binary packages are available.
170 if "--reporter=bwverbose-coverage" in sys.argv:
171     setup_requires.append('trialcoverage >= 0.3.3')
172
173 # stdeb is required to produce Debian files with the "sdist_dsc" command.
174 if "sdist_dsc" in sys.argv:
175     setup_requires.append('stdeb >= 0.3')
176
177 # We no longer have any requirements specific to tests.
178 tests_require=[]
179
180
181 class ShowSupportLib(Command):
182     user_options = []
183     def initialize_options(self):
184         pass
185     def finalize_options(self):
186         pass
187     def run(self):
188         # TODO: --quiet suppresses the 'running show_supportlib' message.
189         # Find a way to do this all the time.
190         print supportlib # TODO windowsy
191
192 class ShowPythonPath(Command):
193     user_options = []
194     def initialize_options(self):
195         pass
196     def finalize_options(self):
197         pass
198     def run(self):
199         # TODO: --quiet suppresses the 'running show_supportlib' message.
200         # Find a way to do this all the time.
201         print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
202
203 class RunWithPythonPath(Command):
204     description = "Run a subcommand with PYTHONPATH set appropriately"
205
206     user_options = [ ("python", "p",
207                       "Treat command string as arguments to a python executable"),
208                      ("command=", "c", "Command to be run"),
209                      ("directory=", "d", "Directory to run the command in"),
210                      ]
211     boolean_options = ["python"]
212
213     def initialize_options(self):
214         self.command = None
215         self.python = False
216         self.directory = None
217     def finalize_options(self):
218         pass
219     def run(self):
220         oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
221         if oldpp == [""]:
222             # grr silly split() behavior
223             oldpp = []
224         os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
225
226         # We must require the command to be safe to split on
227         # whitespace, and have --python and --directory to make it
228         # easier to achieve this.
229
230         command = []
231         if self.python:
232             command.append(sys.executable)
233         if self.command:
234             command.extend(self.command.split())
235         if not command:
236             raise RuntimeError("The --command argument is mandatory")
237         if self.directory:
238             os.chdir(self.directory)
239         if self.verbose:
240             print "command =", " ".join(command)
241         rc = subprocess.call(command)
242         sys.exit(rc)
243
244 class TestMacDiskImage(Command):
245     description = "test the Mac disk image in dmg format (unmaintained)"
246     user_options = []
247
248     def initialize_options(self):
249         pass
250     def finalize_options(self):
251         pass
252     def run(self):
253         import sys
254         sys.path.append(os.path.join('misc', 'build_helpers'))
255         import test_mac_diskimage
256         return test_mac_diskimage.test_mac_diskimage('Allmydata', version=self.distribution.metadata.version)
257
258
259 class Trial(Command):
260     description = "run trial (use 'bin%stahoe debug trial' for the full set of trial options)" % (os.sep,)
261     # This is just a subset of the most useful options, for compatibility.
262     user_options = [ ("rterrors", "e", "Print out tracebacks as soon as they occur."),
263                      ("reporter=", None, "The reporter to use for this test run."),
264                      ("suite=", "s", "Specify the test suite."),
265                      ("quiet", None, "Don't display version numbers and paths of Tahoe dependencies."),
266                    ]
267
268     def initialize_options(self):
269         self.rterrors = False
270         self.reporter = None
271         self.suite = "allmydata"
272         self.quiet = False
273
274     def finalize_options(self):
275         pass
276
277     def run(self):
278         args = [sys.executable, os.path.join('bin', 'tahoe')]
279         if not self.quiet:
280             args.append('--version-and-path')
281         args += ['debug', 'trial']
282         if self.rterrors:
283             args.append('--rterrors')
284         if self.reporter:
285             args.append('--reporter=' + self.reporter)
286         if self.suite:
287             args.append(self.suite)
288         rc = subprocess.call(args)
289         sys.exit(rc)
290
291
292 class MakeExecutable(Command):
293     description = "make the 'bin%stahoe' scripts" % (os.sep,)
294     user_options = []
295
296     def initialize_options(self):
297         pass
298     def finalize_options(self):
299         pass
300     def run(self):
301         bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
302
303         # tahoe.pyscript is really only necessary for Windows, but we also
304         # create it on Unix for consistency.
305         script_names = ["tahoe.pyscript", "tahoe"]
306
307         # Create the tahoe script file under the 'bin' directory. This
308         # file is exactly the same as the 'tahoe-script.template' script
309         # except that the shebang line is rewritten to use our sys.executable
310         # for the interpreter.
311         f = open(bin_tahoe_template, "rU")
312         script_lines = f.readlines()
313         f.close()
314         script_lines[0] = '#!%s\n' % (sys.executable,)
315         for script_name in script_names:
316             tahoe_script = os.path.join("bin", script_name)
317             try:
318                 os.remove(tahoe_script)
319             except Exception:
320                 if os.path.exists(tahoe_script):
321                    raise
322             f = open(tahoe_script, "wb")
323             for line in script_lines:
324                 f.write(line)
325             f.close()
326
327         # chmod +x
328         unix_script = os.path.join("bin", "tahoe")
329         old_mode = stat.S_IMODE(os.stat(unix_script)[stat.ST_MODE])
330         new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
331                                stat.S_IXGRP | stat.S_IRGRP |
332                                stat.S_IXOTH | stat.S_IROTH )
333         os.chmod(unix_script, new_mode)
334
335         old_tahoe_exe = os.path.join("bin", "tahoe.exe")
336         try:
337             os.remove(old_tahoe_exe)
338         except Exception:
339             if os.path.exists(old_tahoe_exe):
340                 raise
341
342
343 class MySdist(sdist.sdist):
344     """ A hook in the sdist command so that we can determine whether this the
345     tarball should be 'SUMO' or not, i.e. whether or not to include the
346     external dependency tarballs. Note that we always include
347     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
348     is included as well.
349     """
350
351     user_options = sdist.sdist.user_options + \
352         [('sumo', 's',
353           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
354          ]
355     boolean_options = ['sumo']
356
357     def initialize_options(self):
358         sdist.sdist.initialize_options(self)
359         self.sumo = False
360
361     def make_distribution(self):
362         # add our extra files to the list just before building the
363         # tarball/zipfile. We override make_distribution() instead of run()
364         # because setuptools.command.sdist.run() does not lend itself to
365         # easy/robust subclassing (the code we need to add goes right smack
366         # in the middle of a 12-line method). If this were the distutils
367         # version, we'd override get_file_list().
368
369         if self.sumo:
370             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
371             # We assume that the user has fetched the tahoe-deps.tar.gz
372             # tarball and unpacked it already.
373             self.filelist.extend([os.path.join("tahoe-deps", fn)
374                                   for fn in os.listdir("tahoe-deps")])
375             # In addition, we want the tarball/zipfile to have -SUMO in the
376             # name, and the unpacked directory to have -SUMO too. The easiest
377             # way to do this is to patch self.distribution and override the
378             # get_fullname() method. (an alternative is to modify
379             # self.distribution.metadata.version, but that also affects the
380             # contents of PKG-INFO).
381             fullname = self.distribution.get_fullname()
382             def get_fullname():
383                 return fullname + "-SUMO"
384             self.distribution.get_fullname = get_fullname
385
386         return sdist.sdist.make_distribution(self)
387
388
389 setup_args = {}
390 if version:
391     setup_args["version"] = version
392
393 setup(name=APPNAME,
394       description='secure, decentralized, fault-tolerant filesystem',
395       long_description=open('README.txt', 'rU').read(),
396       author='the Tahoe-LAFS project',
397       author_email='tahoe-dev@tahoe-lafs.org',
398       url='http://tahoe-lafs.org/',
399       license='GNU GPL', # see README.txt -- there is an alternative licence
400       cmdclass={"show_supportlib": ShowSupportLib,
401                 "show_pythonpath": ShowPythonPath,
402                 "run_with_pythonpath": RunWithPythonPath,
403                 "test_mac_diskimage": TestMacDiskImage,
404                 "trial": Trial,
405                 "make_executable": MakeExecutable,
406                 "sdist": MySdist,
407                 },
408       package_dir = {'':'src'},
409       packages=find_packages("src"),
410       classifiers=trove_classifiers,
411       test_suite="allmydata.test",
412       install_requires=install_requires,
413       tests_require=tests_require,
414       include_package_data=True,
415       setup_requires=setup_requires,
416       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
417       zip_safe=False, # We prefer unzipped for easier access.
418       versionfiles=['src/allmydata/_version.py',],
419       **setup_args
420       )