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, shutil, stat, subprocess, sys, zipfile
13 ##### sys.path management
15 def pylibdir(prefixdir):
16 pyver = "python%d.%d" % (sys.version_info[:2])
17 if sys.platform == "win32":
18 return os.path.join(prefixdir, "Lib", "site-packages")
20 return os.path.join(prefixdir, "lib", pyver, "site-packages")
22 basedir = os.path.dirname(os.path.abspath(__file__))
23 supportlib = pylibdir(os.path.join(basedir, "support"))
26 from ez_setup import use_setuptools
30 # This invokes our own customized version of ez_setup.py to make sure that setuptools
31 # v0.6c12dev (which is our own toothpick of setuptools) is used to build. Note that we can
32 # use any version of setuptools >= 0.6c6 to *run* -- see _auto_deps.py for run-time
33 # dependencies (a.k.a. "install_requires") -- this is only for build-time dependencies
34 # (a.k.a. "setup_requires").
35 use_setuptools(download_delay=0, min_version="0.6c12dev")
37 from setuptools import find_packages, setup
38 from setuptools.command import sdist
39 from setuptools import Command
40 from pkg_resources import require
42 # Make the dependency-version-requirement, which is used by the Makefile at
43 # build-time, also available to the app at runtime:
45 shutil.copyfile("_auto_deps.py", os.path.join("src", "allmydata", "_auto_deps.py"))
48 "Development Status :: 5 - Production/Stable",
49 "Environment :: Console",
50 "Environment :: Web Environment",
51 "License :: OSI Approved :: GNU General Public License (GPL)",
52 "License :: DFSG approved",
53 "License :: Other/Proprietary License",
54 "Intended Audience :: Developers",
55 "Intended Audience :: End Users/Desktop",
56 "Intended Audience :: System Administrators",
57 "Operating System :: Microsoft",
58 "Operating System :: Microsoft :: Windows",
59 "Operating System :: Microsoft :: Windows :: Windows NT/2000",
60 "Operating System :: Unix",
61 "Operating System :: POSIX :: Linux",
62 "Operating System :: POSIX",
63 "Operating System :: MacOS :: MacOS X",
64 "Operating System :: OS Independent",
65 "Natural Language :: English",
66 "Programming Language :: C",
67 "Programming Language :: Python",
68 "Programming Language :: Python :: 2",
69 "Programming Language :: Python :: 2.4",
70 "Programming Language :: Python :: 2.5",
71 "Programming Language :: Python :: 2.6",
73 "Topic :: System :: Systems Administration",
74 "Topic :: System :: Filesystems",
75 "Topic :: System :: Distributed Computing",
76 "Topic :: Software Development :: Libraries",
77 "Topic :: Communications :: Usenet News",
78 "Topic :: System :: Archiving :: Backup",
79 "Topic :: System :: Archiving :: Mirroring",
80 "Topic :: System :: Archiving",
85 """Welcome to the Tahoe project, a secure, decentralized, fault-tolerant
86 filesystem. All of the source code is available under a Free Software, Open
89 This filesystem is encrypted and spread over multiple peers in such a way that
90 it remains available even when some of the peers are unavailable,
91 malfunctioning, or malicious."""
96 # The darcsver command from the darcsver plugin is needed to initialize the distribution's
97 # .version attribute correctly. (It does this either by examining darcs history, or if that
98 # fails by reading the setuptools_trial/_version.py file). darcsver will also write a new
99 # version stamp in setuptools_trial/_version.py, with a version number derived from darcs
100 # history. Note that the setup.cfg file has an "[aliases]" section which enumerates commands
101 # that you might run and specifies that it will run darcsver before each one. If you add
102 # different commands (or if I forgot some that are already in use), you may need to add it to
103 # setup.cfg and configure it to run darcsver before your command, if you want the version number
104 # to be correct when that command runs.
105 # http://pypi.python.org/pypi/darcsver
106 setup_requires.append('darcsver >= 1.2.0')
108 # Nevow requires Twisted to setup, but doesn't declare that requirement in a way that enables
109 # setuptools to satisfy that requirement before Nevow's setup.py tried to "import twisted".
110 # Fortunately we require setuptools_trial to setup and setuptools_trial requires Twisted to
111 # install, so hopefully everything will work out until the Nevow issue is fixed:
112 # http://divmod.org/trac/ticket/2629
113 # setuptools_trial is needed if you want "./setup.py trial" or "./setup.py test" to execute the
114 # tests (and in order to make sure Twisted is installed early enough -- see the paragraph
116 # http://pypi.python.org/pypi/setuptools_trial
117 setup_requires.extend(['setuptools_trial >= 0.5'])
119 # setuptools_darcs is required to produce complete distributions (such as with "sdist" or
120 # "bdist_egg") (unless there is a PKG-INFO file present which shows that this is itself a source
121 # distribution). For simplicity, and because there is some unknown error with setuptools_darcs
122 # when building and testing tahoe all in one python command on some platforms, we always add it
124 # http://pypi.python.org/pypi/setuptools_darcs
125 setup_requires.append('setuptools_darcs >= 1.1.0')
127 class ShowSupportLib(Command):
129 def initialize_options(self):
131 def finalize_options(self):
134 # TODO: --quiet suppresses the 'running show_supportlib' message.
135 # Find a way to do this all the time.
136 print supportlib # TODO windowsy
138 class ShowPythonPath(Command):
140 def initialize_options(self):
142 def finalize_options(self):
145 # TODO: --quiet suppresses the 'running show_supportlib' message.
146 # Find a way to do this all the time.
147 print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
149 class RunWithPythonPath(Command):
150 description = "Run a subcommand with PYTHONPATH set appropriately"
152 user_options = [ ("python", "p",
153 "Treat command string as arguments to a python executable"),
154 ("command=", "c", "Command to be run"),
155 ("directory=", "d", "Directory to run the command in"),
157 boolean_options = ["python"]
159 def initialize_options(self):
162 self.directory = None
163 def finalize_options(self):
166 oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
168 # grr silly split() behavior
170 os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
172 # We must require the command to be safe to split on
173 # whitespace, and have --python and --directory to make it
174 # easier to achieve this.
178 command.append(sys.executable)
180 command.extend(self.command.split())
182 raise RuntimeError("The --command argument is mandatory")
184 os.chdir(self.directory)
186 print "command =", " ".join(command)
187 rc = subprocess.call(command)
190 class CheckAutoDeps(Command):
192 def initialize_options(self):
194 def finalize_options(self):
198 _auto_deps.require_auto_deps()
201 class MakeExecutable(Command):
203 def initialize_options(self):
205 def finalize_options(self):
208 bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
210 # Create the 'tahoe-script.py' file under the 'bin' directory. The 'tahoe-script.py'
211 # file is exactly the same as the 'tahoe-script.template' script except that the shebang
212 # line is rewritten to use our sys.executable for the interpreter. On Windows, create a
213 # tahoe.exe will execute it. On non-Windows, make a symlink to it from 'tahoe'. The
214 # tahoe.exe will be copied from the setuptools egg's cli.exe and this will work from a
215 # zip-safe and non-zip-safe setuptools egg.
216 f = open(bin_tahoe_template, "rU")
217 script_lines = f.readlines()
219 script_lines[0] = "#!%s\n" % sys.executable
220 tahoe_script = os.path.join("bin", "tahoe-script.py")
221 f = open(tahoe_script, "w")
222 for line in script_lines:
225 if sys.platform == "win32":
226 setuptools_egg = require("setuptools")[0].location
227 if os.path.isfile(setuptools_egg):
228 z = zipfile.ZipFile(setuptools_egg, 'r')
229 for filename in z.namelist():
230 if 'cli.exe' in filename:
231 cli_exe = z.read(filename)
233 cli_exe = os.path.join(setuptools_egg, 'setuptools', 'cli.exe')
234 tahoe_exe = os.path.join("bin", "tahoe.exe")
235 if os.path.isfile(setuptools_egg):
236 f = open(tahoe_exe, 'wb')
240 shutil.copy(cli_exe, tahoe_exe)
243 os.remove(os.path.join('bin', 'tahoe'))
245 # okay, probably it was already gone
247 os.symlink('tahoe-script.py', os.path.join('bin', 'tahoe'))
249 # chmod +x bin/tahoe-script.py
250 old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE])
251 new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
252 stat.S_IXGRP | stat.S_IRGRP |
253 stat.S_IXOTH | stat.S_IROTH )
254 os.chmod(tahoe_script, new_mode)
256 class MySdist(sdist.sdist):
257 """ A hook in the sdist command so that we can determine whether this the
258 tarball should be 'SUMO' or not, i.e. whether or not to include the
259 external dependency tarballs. Note that we always include
260 misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
264 user_options = sdist.sdist.user_options + \
266 "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
268 boolean_options = ['sumo']
270 def initialize_options(self):
271 sdist.sdist.initialize_options(self)
274 def make_distribution(self):
275 # add our extra files to the list just before building the
276 # tarball/zipfile. We override make_distribution() instead of run()
277 # because setuptools.command.sdist.run() does not lend itself to
278 # easy/robust subclassing (the code we need to add goes right smack
279 # in the middle of a 12-line method). If this were the distutils
280 # version, we'd override get_file_list().
283 # If '--sumo' was specified, include tahoe-deps/* in the sdist.
284 # We assume that the user has fetched the tahoe-deps.tar.gz
285 # tarball and unpacked it already.
286 self.filelist.extend([os.path.join("tahoe-deps", fn)
287 for fn in os.listdir("tahoe-deps")])
288 # In addition, we want the tarball/zipfile to have -SUMO in the
289 # name, and the unpacked directory to have -SUMO too. The easiest
290 # way to do this is to patch self.distribution and override the
291 # get_fullname() method. (an alternative is to modify
292 # self.distribution.metadata.version, but that also affects the
293 # contents of PKG-INFO).
294 fullname = self.distribution.get_fullname()
296 return fullname + "-SUMO"
297 self.distribution.get_fullname = get_fullname
299 return sdist.sdist.make_distribution(self)
301 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and the
302 # _auto_deps.install_requires list, which is used in the call to setup() below.
303 from _auto_deps import install_requires
305 setup(name='allmydata-tahoe',
306 description='secure, decentralized, fault-tolerant filesystem',
307 long_description=LONG_DESCRIPTION,
308 author='the allmydata.org Tahoe project',
309 author_email='tahoe-dev@allmydata.org',
310 url='http://allmydata.org/',
312 cmdclass={"show_supportlib": ShowSupportLib,
313 "show_pythonpath": ShowPythonPath,
314 "run_with_pythonpath": RunWithPythonPath,
315 "check_auto_deps": CheckAutoDeps,
316 "make_executable": MakeExecutable,
319 package_dir = {'':'src'},
320 packages=find_packages("src"),
321 classifiers=trove_classifiers,
322 test_suite="allmydata.test",
323 install_requires=install_requires,
324 include_package_data=True,
325 setup_requires=setup_requires,
326 entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
327 zip_safe=False, # We prefer unzipped for easier access.