]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
534feed6f5cbe964230be5a7b0a3e0f444357f56
[tahoe-lafs/tahoe-lafs.git] / setup.py
1 #! /usr/bin/env python
2
3 # Allmydata Tahoe -- secure, distributed storage grid
4 #
5 # Copyright (C) 2008 Allmydata, Inc.
6 #
7 # This file is part of tahoe.
8 #
9 # See the docs/about.html file for licensing information.
10
11 import os, re, sys, stat, subprocess
12
13 ##### sys.path management
14
15 basedir = os.path.dirname(os.path.abspath(__file__))
16 pyver = "python%d.%d" % (sys.version_info[:2])
17 if sys.platform == "win32":
18     supportlib = os.path.join("support", "Lib", "site-packages")
19 else:
20     supportlib = os.path.join("support", "lib", pyver, "site-packages")
21 supportlib = os.path.join(basedir, supportlib)
22
23 def add_tahoe_paths():
24     """Modify sys.path and PYTHONPATH to include Tahoe and supporting libraries
25
26     The first step towards building Tahoe is to run::
27
28       python setup.py build_tahoe
29
30     which is the equivalent of::
31
32       mkdir -p $(BASEDIR)/support/lib/python2.5/site-packages
33        (or cygpath equivalent)
34       setup.py develop --multi-version --prefix=$(BASEDIR)/support
35
36     This installs .eggs for any dependent libraries that aren't already
37     available on the system, into support/lib/pythonN.N/site-packages (or
38     support/Lib/site-packages on windows). It also adds an .egg-link for
39     Tahoe itself into the same directory.
40
41     We add this directory to os.environ['PYTHONPATH'], so that any child
42     processes we spawn will be able to use these packages.
43
44     When the setuptools site.py sees that supportlib in PYTHONPATH, it scans
45     through it for .egg and .egg-link entries, and adds them to sys.path .
46     Since python has already processed all the site.py files by the time we
47     get here, we perform this same sort of processing ourselves: this makes
48     tahoe (and dependency libraries) available to code within setup.py
49     itself. This is used by the 'setup.py trial' subcommand, which invokes
50     trial directly rather than spawning a subprocess (this is easier than
51     locating the 'trial' executable, especially when Twisted was installed as
52     a dependent library).
53
54     We'll need to add these .eggs to sys.path before importing anything that
55     isn't a part of stdlib. All the directories that we add this way are put
56     at the start of sys.path, so they will override anything that was present
57     on the system (and perhaps found lacking by the setuptools requirements
58     expressed in _auto_deps.py).
59     """
60
61     extra_syspath_items = []
62     extra_pythonpath_items = []
63
64     extra_syspath_items.append(supportlib)
65     extra_pythonpath_items.append(supportlib)
66
67     # Since we use setuptools to populate that directory, there will be a
68     # number of .egg and .egg-link entries there. Add all of them to
69     # sys.path, since that what the setuptools site.py would do if it
70     # encountered them at process start time. Without this step, the rest of
71     # this process would be unable to use the packages installed there. We
72     # don't need to add them to PYTHONPATH, since the site.py present there
73     # will add them when the child process starts up.
74
75     if os.path.isdir(supportlib):
76         for fn in os.listdir(supportlib):
77             if fn.endswith(".egg"):
78                 extra_syspath_items.append(os.path.join(supportlib, fn))
79
80     # We also add our src/ directory, since that's where all the Tahoe code
81     # lives. This matches what site.py does when it sees the .egg-link file
82     # that is written to the support dir by an invocation of our 'setup.py
83     # develop' command.
84     extra_syspath_items.append(os.path.join(basedir, "src"))
85
86     # and we put an extra copy of everything from PYTHONPATH in front, so
87     # that it is possible to override the packages that setuptools downloads
88     # with alternate versions, by doing e.g. "PYTHONPATH=foo python setup.py
89     # trial"
90     oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
91     if oldpp == [""]:
92         # grr silly split() behavior
93         oldpp = []
94     extra_syspath_items = oldpp + extra_syspath_items
95
96     sys.path = extra_syspath_items + sys.path
97
98     # We also provide it to any child processes we spawn, via
99     # os.environ["PYTHONPATH"]
100     os.environ["PYTHONPATH"] = os.pathsep.join(oldpp + extra_pythonpath_items)
101
102 # add_tahoe_paths() must be called before use_setuptools() is called. I don't
103 # know why. If it isn't, then a later pkg_resources.requires(pycryptopp) call
104 # fails because an old version (in /usr/lib) was already loaded.
105 add_tahoe_paths()
106
107 try:
108     from ez_setup import use_setuptools
109 except ImportError:
110     pass
111 else:
112     # This invokes our own customized version of ez_setup.py to make sure that
113     # setuptools >= v0.6c8 (a.k.a. v0.6-final) is installed.
114
115     # setuptools < v0.6c8 doesn't handle eggs which get installed into the CWD
116     # as a result of being transitively depended on in a setup_requires, but
117     # then are needed for the installed code to run, i.e. in an
118     # install_requires.
119     use_setuptools(download_delay=0, min_version="0.6c8")
120
121 from setuptools import find_packages, setup
122 from setuptools.command import sdist
123 from distutils.core import Command
124
125 import pkg_resources
126 pkg_resources.require('setuptools_trial')
127 from setuptools_trial.setuptools_trial import TrialTest
128
129 # Make the dependency-version-requirement, which is used by the Makefile at
130 # build-time, also available to the app at runtime:
131 import shutil
132 shutil.copyfile("_auto_deps.py", os.path.join("src", "allmydata", "_auto_deps.py"))
133
134 trove_classifiers=[
135     "Development Status :: 5 - Production/Stable",
136     "Environment :: Console",
137     "Environment :: Web Environment",
138     "License :: OSI Approved :: GNU General Public License (GPL)",
139     "License :: DFSG approved",
140     "License :: Other/Proprietary License",
141     "Intended Audience :: Developers",
142     "Intended Audience :: End Users/Desktop",
143     "Intended Audience :: System Administrators",
144     "Operating System :: Microsoft",
145     "Operating System :: Microsoft :: Windows",
146     "Operating System :: Microsoft :: Windows :: Windows NT/2000",
147     "Operating System :: Unix",
148     "Operating System :: POSIX :: Linux",
149     "Operating System :: POSIX",
150     "Operating System :: MacOS :: MacOS X",
151     "Operating System :: OS Independent",
152     "Natural Language :: English",
153     "Programming Language :: C",
154     "Programming Language :: Python",
155     "Programming Language :: Python :: 2",
156     "Programming Language :: Python :: 2.4",
157     "Programming Language :: Python :: 2.5",
158     "Topic :: Utilities",
159     "Topic :: System :: Systems Administration",
160     "Topic :: System :: Filesystems",
161     "Topic :: System :: Distributed Computing",
162     "Topic :: Software Development :: Libraries",
163     "Topic :: Communications :: Usenet News",
164     "Topic :: System :: Archiving :: Backup",
165     "Topic :: System :: Archiving :: Mirroring",
166     "Topic :: System :: Archiving",
167     ]
168
169
170 VERSIONFILE = "src/allmydata/_version.py"
171 verstr = "unknown"
172 try:
173     verstrline = open(VERSIONFILE, "rt").read()
174 except EnvironmentError:
175     pass # Okay, there is no version file.
176 else:
177     VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
178     mo = re.search(VSRE, verstrline, re.M)
179     if mo:
180         verstr = mo.group(1)
181     else:
182         print "unable to find version in %s" % (VERSIONFILE,)
183         raise RuntimeError("if %s.py exists, it is required to be well-formed" % (VERSIONFILE,))
184
185 LONG_DESCRIPTION=\
186 """Welcome to the Tahoe project, a secure, decentralized, fault-tolerant
187 filesystem.  All of the source code is available under a Free Software, Open
188 Source licence.
189
190 This filesystem is encrypted and spread over multiple peers in such a way that
191 it remains available even when some of the peers are unavailable,
192 malfunctioning, or malicious."""
193
194
195 setup_requires = []
196
197 # Nevow requires Twisted to setup, but doesn't declare that requirement in a way that enables
198 # setuptools to satisfy that requirement before Nevow's setup.py tried to "import twisted".
199 setup_requires.extend(['Twisted >= 2.4.0', 'setuptools_trial'])
200
201 # darcsver is needed only if you want "./setup.py darcsver" to write a new
202 # version stamp in src/allmydata/_version.py, with a version number derived from
203 # darcs history.
204 # http://pypi.python.org/pypi/darcsver
205 if 'darcsver' in sys.argv[1:]:
206     setup_requires.append('darcsver >= 1.1.5')
207
208 # setuptools_trial is needed only if you want "./setup.py trial" to execute the tests.
209 # http://pypi.python.org/pypi/setuptools_trial
210 if 'trial' in sys.argv[1:]:
211     setup_requires.append('setuptools_trial >= 0.2')
212
213 # setuptools_darcs is required to produce complete distributions (such as with
214 # "sdist" or "bdist_egg"), unless there is a PKG-INFO file present which shows
215 # that this is itself a source distribution.
216 # http://pypi.python.org/pypi/setuptools_darcs
217 if not os.path.exists('PKG-INFO'):
218     setup_requires.append('setuptools_darcs >= 1.1.0')
219
220 class ShowSupportLib(Command):
221     user_options = []
222     def initialize_options(self):
223         pass
224     def finalize_options(self):
225         pass
226     def run(self):
227         # TODO: --quiet suppresses the 'running show_supportlib' message.
228         # Find a way to do this all the time.
229         print supportlib # TODO windowsy
230
231 class ShowPythonPath(Command):
232     user_options = []
233     def initialize_options(self):
234         pass
235     def finalize_options(self):
236         pass
237     def run(self):
238         # TODO: --quiet suppresses the 'running show_supportlib' message.
239         # Find a way to do this all the time.
240         print "PYTHONPATH=%s" % os.environ["PYTHONPATH"]
241
242 class RunWithPythonPath(Command):
243     description = "Run a subcommand with PYTHONPATH set appropriately"
244
245     user_options = [ ("python", "p",
246                       "Treat command string as arguments to a python executable"),
247                      ("command=", "c", "Command to be run"),
248                      ("directory=", "d", "Directory to run the command in"),
249                      ]
250     boolean_options = ["python"]
251
252     def initialize_options(self):
253         self.command = None
254         self.python = False
255         self.directory = None
256     def finalize_options(self):
257         pass
258     def run(self):
259         # os.environ['PYTHONPATH'] is already set by add_tahoe_paths, so we
260         # just need to exec() their command. We must require the command to
261         # be safe to split on whitespace, and have --python and --directory
262         # to make it easier to achieve this.
263         command = []
264         if self.python:
265             command.append(sys.executable)
266         if self.command:
267             command.extend(self.command.split())
268         if not command:
269             raise RuntimeError("The --command argument is mandatory")
270         if self.directory:
271             os.chdir(self.directory)
272         if self.verbose:
273             print "command =", " ".join(command)
274         rc = subprocess.call(command)
275         sys.exit(rc)
276
277 class CheckAutoDeps(Command):
278     user_options = []
279     def initialize_options(self):
280         pass
281     def finalize_options(self):
282         pass
283     def run(self):
284         import _auto_deps
285         _auto_deps.require_auto_deps()
286
287
288 class BuildTahoe(Command):
289     user_options = []
290     def initialize_options(self):
291         pass
292     def finalize_options(self):
293         pass
294     def run(self):
295         # chmod +x bin/tahoe
296         bin_tahoe = os.path.join("bin", "tahoe")
297         old_mode = stat.S_IMODE(os.stat(bin_tahoe)[stat.ST_MODE])
298         new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
299                                stat.S_IXGRP | stat.S_IRGRP |
300                                stat.S_IXOTH | stat.S_IROTH )
301         os.chmod(bin_tahoe, new_mode)
302
303         # 'setup.py develop --multi-version --prefix SUPPORT' will complain if SUPPORTLIB is
304         # not on PYTHONPATH, because it thinks you are installing to a place
305         # that will not be searched at runtime (which is true, except that we
306         # add SUPPORTLIB to PYTHONPATH to run tests, etc). So set up
307         # PYTHONPATH now, then spawn a 'setup.py develop' command. Also, we
308         # have to create the directory ourselves.
309         if not os.path.isdir(supportlib):
310             os.makedirs(supportlib)
311
312         # command = [sys.executable, "setup.py", "develop", "--multi-version", "--prefix", "support"]
313         command = [sys.executable, "setup.py", "develop", "--prefix", "support"]
314         if sys.platform == "linux2":
315             # workaround for tahoe #229 / setuptools #17, on debian
316             command.extend(["--site-dirs", "/var/lib/python-support/" + pyver])
317         elif sys.platform == "darwin":
318             # this probably only applies to leopard 10.5, possibly only 10.5.5
319             sd = "/System/Library/Frameworks/Python.framework/Versions/2.5/Extras/lib/python"
320             command.extend(["--site-dirs", sd])
321         print "Command:", " ".join(command)
322         rc = subprocess.call(command)
323         if rc < 0:
324             print >>sys.stderr, "'setup.py develop' terminated by signal", -rc
325             sys.exit(1)
326         elif rc > 0:
327             print >>sys.stderr, "'setup.py develop' exited with rc", rc
328             sys.exit(rc)
329
330 class Trial(TrialTest):
331     # Custom sub-class of the TrialTest class from the setuptools_trial
332     # plugin so that we can ensure certain options are set by default.
333     #
334     # Examples:
335     #  setup.py trial    # run all tests
336     #  setup.py trial -a allmydata.test.test_util   # run some tests
337     #  setup.py trial -a '--reporter=text allmydata.test.test_util' #other args
338
339
340     def initialize_options(self):
341         TrialTest.initialize_options(self)
342
343         # We want to set the reactor to 'poll', because of bug #402
344         # (twisted bug #3218).
345         if sys.platform in ("linux2", "cygwin"):
346             # poll on linux2 to avoid #402 problems with select
347             # poll on cygwin since selectreactor runs out of fds
348             self.reactor = "poll"
349
350
351 class MySdist(sdist.sdist):
352     """ A hook in the sdist command so that we can determine whether this the
353     tarball should be 'SUMO' or not, i.e. whether or not to include the
354     external dependency tarballs. Note that we always include
355     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
356     is included as well.
357     """
358
359     user_options = sdist.sdist.user_options + \
360         [('sumo', 's',
361           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
362          ]
363     boolean_options = ['sumo']
364
365     def initialize_options(self):
366         sdist.sdist.initialize_options(self)
367         self.sumo = False
368
369     def make_distribution(self):
370         # add our extra files to the list just before building the
371         # tarball/zipfile. We override make_distribution() instead of run()
372         # because setuptools.command.sdist.run() does not lend itself to
373         # easy/robust subclassing (the code we need to add goes right smack
374         # in the middle of a 12-line method). If this were the distutils
375         # version, we'd override get_file_list().
376
377         if self.sumo:
378             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
379             # We assume that the user has fetched the tahoe-deps.tar.gz
380             # tarball and unpacked it already.
381             self.filelist.extend([os.path.join("tahoe-deps", fn)
382                                   for fn in os.listdir("tahoe-deps")])
383             # In addition, we want the tarball/zipfile to have -SUMO in the
384             # name, and the unpacked directory to have -SUMO too. The easiest
385             # way to do this is to patch self.distribution and override the
386             # get_fullname() method. (an alternative is to modify
387             # self.distribution.metadata.version, but that also affects the
388             # contents of PKG-INFO).
389             fullname = self.distribution.get_fullname()
390             def get_fullname():
391                 return fullname + "-SUMO"
392             self.distribution.get_fullname = get_fullname
393
394         return sdist.sdist.make_distribution(self)
395
396 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
397 # the _auto_deps.install_requires list, which is used in the call to setup()
398 # at the end of this file
399 from _auto_deps import install_requires
400
401 setup(name='allmydata-tahoe',
402       version=verstr,
403       description='secure, decentralized, fault-tolerant filesystem',
404       long_description=LONG_DESCRIPTION,
405       author='the allmydata.org Tahoe project',
406       author_email='tahoe-dev@allmydata.org',
407       url='http://allmydata.org/',
408       license='GNU GPL',
409       cmdclass={"show_supportlib": ShowSupportLib,
410                 "show_pythonpath": ShowPythonPath,
411                 "run_with_pythonpath": RunWithPythonPath,
412                 "check_auto_deps": CheckAutoDeps,
413                 "build_tahoe": BuildTahoe,
414                 "trial": Trial,
415                 "sdist": MySdist,
416                 },
417       package_dir = {'':'src'},
418       packages=find_packages("src"),
419       classifiers=trove_classifiers,
420       test_suite="allmydata.test",
421       install_requires=install_requires,
422       include_package_data=True,
423       setup_requires=setup_requires,
424       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
425       zip_safe=False, # We prefer unzipped for easier access.
426       )