3 # Allmydata Tahoe -- secure, distributed storage grid
5 # Copyright (C) 2008 Allmydata, Inc.
7 # This file is part of tahoe.
9 # See the docs/about.html file for licensing information.
11 import os, re, sys, stat, subprocess
13 ##### sys.path management
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")
20 supportlib = os.path.join("support", "lib", pyver, "site-packages")
21 supportlib = os.path.join(basedir, supportlib)
23 def add_tahoe_paths():
24 """Modify sys.path and PYTHONPATH to include Tahoe and supporting libraries
26 The first step towards building Tahoe is to run::
28 python setup.py build_tahoe
30 which is the equivalent of::
32 mkdir -p $(BASEDIR)/support/lib/python2.5/site-packages
33 (or cygpath equivalent)
34 setup.py develop --prefix=$(BASEDIR)/support
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.
41 We add this directory to os.environ['PYTHONPATH'], so that any child
42 processes we spawn will be able to use these packages.
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
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).
61 extra_syspath_items = []
62 extra_pythonpath_items = []
64 extra_syspath_items.append(supportlib)
65 extra_pythonpath_items.append(supportlib)
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.
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))
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
84 extra_syspath_items.append(os.path.join(basedir, "src"))
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
90 oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
92 # grr silly split() behavior
94 extra_syspath_items = oldpp + extra_syspath_items
96 sys.path = extra_syspath_items + sys.path
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)
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.
108 from ez_setup import use_setuptools
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.
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
119 use_setuptools(download_delay=0, min_version="0.6c8")
121 from setuptools import find_packages, setup
122 from setuptools.command import sdist
123 from distutils.core import Command
125 # Make the dependency-version-requirement, which is used by the Makefile at
126 # build-time, also available to the app at runtime:
128 shutil.copyfile("_auto_deps.py", os.path.join("src", "allmydata", "_auto_deps.py"))
131 "Development Status :: 5 - Production/Stable",
132 "Environment :: Console",
133 "Environment :: Web Environment",
134 "License :: OSI Approved :: GNU General Public License (GPL)",
135 "License :: DFSG approved",
136 "License :: Other/Proprietary License",
137 "Intended Audience :: Developers",
138 "Intended Audience :: End Users/Desktop",
139 "Intended Audience :: System Administrators",
140 "Operating System :: Microsoft",
141 "Operating System :: Microsoft :: Windows",
142 "Operating System :: Unix",
143 "Operating System :: POSIX :: Linux",
144 "Operating System :: POSIX",
145 "Operating System :: MacOS :: MacOS X",
146 "Operating System :: Microsoft :: Windows :: Windows NT/2000",
147 "Operating System :: OS Independent",
148 "Natural Language :: English",
149 "Programming Language :: C",
150 "Programming Language :: Python",
151 "Topic :: Utilities",
152 "Topic :: System :: Systems Administration",
153 "Topic :: System :: Filesystems",
154 "Topic :: System :: Distributed Computing",
155 "Topic :: Software Development :: Libraries",
156 "Topic :: Communications :: Usenet News",
157 "Topic :: System :: Archiving :: Backup",
158 "Topic :: System :: Archiving :: Mirroring",
159 "Topic :: System :: Archiving",
163 VERSIONFILE = "src/allmydata/_version.py"
166 verstrline = open(VERSIONFILE, "rt").read()
167 except EnvironmentError:
168 pass # Okay, there is no version file.
170 VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
171 mo = re.search(VSRE, verstrline, re.M)
175 print "unable to find version in %s" % (VERSIONFILE,)
176 raise RuntimeError("if %s.py exists, it is required to be well-formed" % (VERSIONFILE,))
179 """Welcome to the Tahoe project, a secure, decentralized, fault-tolerant
180 filesystem. All of the source code is available under a Free Software, Open
183 This filesystem is encrypted and spread over multiple peers in such a way that
184 it remains available even when some of the peers are unavailable,
185 malfunctioning, or malicious."""
188 # Default setup_requires are pyutil for the Windows installer builder(see
189 # misc/sub-ver.py) and Twisted for the tests.
190 #setup_requires = ['pyutil >= 1.3.16', 'Twisted >= 2.4.0']
193 # darcsver is needed only if you want "./setup.py darcsver" to write a new
194 # version stamp in src/allmydata/_version.py, with a version number derived from
196 # http://pypi.python.org/pypi/darcsver
197 if 'darcsver' in sys.argv[1:]:
198 setup_requires.append('darcsver >= 1.1.5')
200 # setuptools_darcs is required to produce complete distributions (such as with
201 # "sdist" or "bdist_egg"), unless there is a PKG-INFO file present which shows
202 # that this is itself a source distribution.
203 # http://pypi.python.org/pypi/setuptools_darcs
204 if not os.path.exists('PKG-INFO'):
205 setup_requires.append('setuptools_darcs >= 1.1.0')
207 class ShowSupportLib(Command):
209 def initialize_options(self):
211 def finalize_options(self):
214 # TODO: --quiet suppresses the 'running show_supportlib' message.
215 # Find a way to do this all the time.
216 print supportlib # TODO windowsy
218 class ShowPythonPath(Command):
220 def initialize_options(self):
222 def finalize_options(self):
225 # TODO: --quiet suppresses the 'running show_supportlib' message.
226 # Find a way to do this all the time.
227 print "PYTHONPATH=%s" % os.environ["PYTHONPATH"]
229 class RunWithPythonPath(Command):
230 description = "Run a subcommand with PYTHONPATH set appropriately"
232 user_options = [ ("python", "p",
233 "Treat command string as arguments to a python executable"),
234 ("command=", "c", "Command to be run"),
235 ("directory=", "d", "Directory to run the command in"),
237 boolean_options = ["python"]
239 def initialize_options(self):
242 self.directory = None
243 def finalize_options(self):
246 # os.environ['PYTHONPATH'] is already set by add_tahoe_paths, so we
247 # just need to exec() their command. We must require the command to
248 # be safe to split on whitespace, and have --python and --directory
249 # to make it easier to achieve this.
252 command.append(sys.executable)
254 command.extend(self.command.split())
256 raise RuntimeError("The --command argument is mandatory")
258 os.chdir(self.directory)
260 print "command =", " ".join(command)
261 rc = subprocess.call(command)
264 class CheckAutoDeps(Command):
266 def initialize_options(self):
268 def finalize_options(self):
272 _auto_deps.require_auto_deps()
275 class BuildTahoe(Command):
277 def initialize_options(self):
279 def finalize_options(self):
283 bin_tahoe = os.path.join("bin", "tahoe")
284 old_mode = stat.S_IMODE(os.stat(bin_tahoe)[stat.ST_MODE])
285 new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
286 stat.S_IXGRP | stat.S_IRGRP |
287 stat.S_IXOTH | stat.S_IROTH )
288 os.chmod(bin_tahoe, new_mode)
290 # 'setup.py develop --prefix SUPPORT' will complain if SUPPORTLIB is
291 # not on PYTHONPATH, because it thinks you are installing to a place
292 # that will not be searched at runtime (which is true, except that we
293 # add SUPPORTLIB to PYTHONPATH to run tests, etc). So set up
294 # PYTHONPATH now, then spawn a 'setup.py develop' command. Also, we
295 # have to create the directory ourselves.
296 if not os.path.isdir(supportlib):
297 os.makedirs(supportlib)
299 command = [sys.executable, "setup.py", "develop", "--prefix", "support"]
300 if sys.platform == "linux2":
301 # workaround for tahoe #229 / setuptools #17, on debian
302 command.extend(["--site-dirs", "/var/lib/python-support/" + pyver])
303 print "Command:", " ".join(command)
304 rc = subprocess.call(command)
306 print >>sys.stderr, "'setup.py develop' terminated by signal", -rc
309 print >>sys.stderr, "'setup.py develop' exited with rc", rc
312 class Trial(Command):
313 # Unlike 'build' and 'bdist_egg', the 'trial' subcommand cannot be run in
314 # conjunction with other subcommands.
316 # The '-a' argument is split on whitespace and passed into trial. (the
317 # distutils parser does not give subcommands access to the rest of
318 # sys.argv, so unfortunately we cannot just do something like:
319 # setup.py trial --reporter=text allmydata.test.test_util
322 # setup.py trial # run all tests
323 # setup.py trial -a allmydata.test.test_util # run some tests
324 # setup.py trial -a '--reporter=text allmydata.test.test_util' #other args
326 description = "Run unit tests via trial"
328 user_options = [ ("args=", "a", "Argument string to pass to trial: setup.py trial -a allmydata.test.test_util"),
330 def initialize_options(self):
331 self.args = "allmydata"
332 def finalize_options(self):
336 # make sure Twisted is available (for trial itself), and both the
337 # Tahoe source code and our dependent libraries are available (so
338 # that trial has some test code to work with)
340 from twisted.scripts import trial
342 args = self.args.strip().split()
344 # one wrinkle: we want to set the reactor here, because of bug #402
345 # (twisted bug #3218). We just jam in a "--reactor poll" at the start
346 # of the arglist. This does not permit the reactor to be overridden,
348 if sys.platform in ("linux2", "cygwin"):
349 # poll on linux2 to avoid #402 problems with select
350 # poll on cygwin since selectreactor runs out of fds
351 args = ["--reactor", "poll"] + args
353 # zooko also had os.environ["PYTHONUNBUFFERED"]="1" and
354 # args.append("--rterrors")
356 sys.argv = ["trial"] + args
358 print "To run this test directly, use:"
359 print "PYTHONPATH=%s %s" % (os.environ["PYTHONPATH"],
362 print "(run setup.py with -vv for trial command-line details)"
363 trial.run() # this does sys.exit
366 class MySdist(sdist.sdist):
367 """ A hook in the sdist command so that we can determine whether this the
368 tarball should be 'SUMO' or not, i.e. whether or not to include the
369 external dependency tarballs. Note that we always include
370 misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
374 user_options = sdist.sdist.user_options + \
376 "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
378 boolean_options = ['sumo']
380 def initialize_options(self):
381 sdist.sdist.initialize_options(self)
384 def make_distribution(self):
385 # add our extra files to the list just before building the
386 # tarball/zipfile. We override make_distribution() instead of run()
387 # because setuptools.command.sdist.run() does not lend itself to
388 # easy/robust subclassing (the code we need to add goes right smack
389 # in the middle of a 12-line method). If this were the distutils
390 # version, we'd override get_file_list().
393 # If '--sumo' was specified, include tahoe-deps/* in the sdist.
394 # We assume that the user has fetched the tahoe-deps.tar.gz
395 # tarball and unpacked it already.
396 self.filelist.extend([os.path.join("tahoe-deps", fn)
397 for fn in os.listdir("tahoe-deps")])
398 # In addition, we want the tarball/zipfile to have -SUMO in the
399 # name, and the unpacked directory to have -SUMO too. The easiest
400 # way to do this is to patch self.distribution and override the
401 # get_fullname() method. (an alternative is to modify
402 # self.distribution.metadata.version, but that also affects the
403 # contents of PKG-INFO).
404 fullname = self.distribution.get_fullname()
406 return fullname + "-SUMO"
407 self.distribution.get_fullname = get_fullname
409 return sdist.sdist.make_distribution(self)
411 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
412 # the _auto_deps.install_requires list, which is used in the call to setup()
413 # at the end of this file
414 from _auto_deps import install_requires
416 setup(name='allmydata-tahoe',
418 description='secure, decentralized, fault-tolerant filesystem',
419 long_description=LONG_DESCRIPTION,
420 author='the allmydata.org Tahoe project',
421 author_email='tahoe-dev@allmydata.org',
422 url='http://allmydata.org/',
424 cmdclass={"show_supportlib": ShowSupportLib,
425 "show_pythonpath": ShowPythonPath,
426 "run_with_pythonpath": RunWithPythonPath,
427 "check_auto_deps": CheckAutoDeps,
428 "build_tahoe": BuildTahoe,
432 package_dir = {'':'src'},
433 packages=find_packages("src"),
434 classifiers=trove_classifiers,
435 test_suite="allmydata.test",
436 install_requires=install_requires,
437 include_package_data=True,
438 setup_requires=setup_requires,
439 entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
440 zip_safe=False, # We prefer unzipped for easier access.