]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
4c1943013f4f036e8541bfc9448e422e985058e5
[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, shutil, stat, subprocess, sys, zipfile
12
13 ##### sys.path management
14
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")
19     else:
20         return os.path.join(prefixdir, "lib", pyver, "site-packages")
21
22 basedir = os.path.dirname(os.path.abspath(__file__))
23 supportlib = pylibdir(os.path.join(basedir, "support"))
24
25
26 for i in range(len(sys.argv)):
27     arg = sys.argv[i]
28     prefixdir = None
29     if arg.startswith("--prefix="):
30         prefixdir = arg[len("--prefix="):]
31     if arg == "--prefix":
32         if len(sys.argv) > i+1:
33             prefixdir = sys.argv[i+1]
34
35     if prefixdir:
36         libdir = pylibdir(prefixdir)
37         try:
38             os.makedirs(libdir)
39         except EnvironmentError, le:
40             # Okay, maybe the dir was already there.
41             pass
42         sys.path.append(libdir)
43         pp = os.environ.get('PYTHONPATH','').split(os.pathsep)
44         pp.append(libdir)
45         os.environ['PYTHONPATH'] = os.pathsep.join(pp)
46
47     if arg.startswith("develop"):
48         if sys.platform == "linux2":
49             # workaround for tahoe #229 / setuptools #17, on debian
50             sys.argv.extend(["--site-dirs", "/var/lib/python-support/python%d.%d" % (sys.version_info[:2])])
51         elif sys.platform == "darwin":
52             # this probably only applies to leopard 10.5, possibly only 10.5.5
53             sd = "/System/Library/Frameworks/Python.framework/Versions/%d.%d/Extras/lib/python" % (sys.version_info[:2])
54             sys.argv.extend(["--site-dirs", sd])
55
56 try:
57     from ez_setup import use_setuptools
58 except ImportError:
59     pass
60 else:
61     # This invokes our own customized version of ez_setup.py to make sure that
62     # setuptools >= v0.6c8 (a.k.a. v0.6-final) is installed.
63
64     # setuptools < v0.6c8 doesn't handle eggs which get installed into the CWD
65     # as a result of being transitively depended on in a setup_requires, but
66     # then are needed for the installed code to run, i.e. in an
67     # install_requires.
68     use_setuptools(download_delay=0, min_version="0.6c10dev")
69
70 from setuptools import find_packages, setup
71 from setuptools.command import sdist
72 from distutils.core import Command
73 from pkg_resources import require
74
75 # Make the dependency-version-requirement, which is used by the Makefile at
76 # build-time, also available to the app at runtime:
77 import shutil
78 shutil.copyfile("_auto_deps.py", os.path.join("src", "allmydata", "_auto_deps.py"))
79
80 trove_classifiers=[
81     "Development Status :: 5 - Production/Stable",
82     "Environment :: Console",
83     "Environment :: Web Environment",
84     "License :: OSI Approved :: GNU General Public License (GPL)",
85     "License :: DFSG approved",
86     "License :: Other/Proprietary License",
87     "Intended Audience :: Developers",
88     "Intended Audience :: End Users/Desktop",
89     "Intended Audience :: System Administrators",
90     "Operating System :: Microsoft",
91     "Operating System :: Microsoft :: Windows",
92     "Operating System :: Microsoft :: Windows :: Windows NT/2000",
93     "Operating System :: Unix",
94     "Operating System :: POSIX :: Linux",
95     "Operating System :: POSIX",
96     "Operating System :: MacOS :: MacOS X",
97     "Operating System :: OS Independent",
98     "Natural Language :: English",
99     "Programming Language :: C",
100     "Programming Language :: Python",
101     "Programming Language :: Python :: 2",
102     "Programming Language :: Python :: 2.4",
103     "Programming Language :: Python :: 2.5",
104     "Topic :: Utilities",
105     "Topic :: System :: Systems Administration",
106     "Topic :: System :: Filesystems",
107     "Topic :: System :: Distributed Computing",
108     "Topic :: Software Development :: Libraries",
109     "Topic :: Communications :: Usenet News",
110     "Topic :: System :: Archiving :: Backup",
111     "Topic :: System :: Archiving :: Mirroring",
112     "Topic :: System :: Archiving",
113     ]
114
115
116 VERSIONFILE = "src/allmydata/_version.py"
117 verstr = "unknown"
118 try:
119     verstrline = open(VERSIONFILE, "rt").read()
120 except EnvironmentError:
121     pass # Okay, there is no version file.
122 else:
123     VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
124     mo = re.search(VSRE, verstrline, re.M)
125     if mo:
126         verstr = mo.group(1)
127     else:
128         print "unable to find version in %s" % (VERSIONFILE,)
129         raise RuntimeError("if %s.py exists, it is required to be well-formed" % (VERSIONFILE,))
130
131 LONG_DESCRIPTION=\
132 """Welcome to the Tahoe project, a secure, decentralized, fault-tolerant
133 filesystem.  All of the source code is available under a Free Software, Open
134 Source licence.
135
136 This filesystem is encrypted and spread over multiple peers in such a way that
137 it remains available even when some of the peers are unavailable,
138 malfunctioning, or malicious."""
139
140
141 setup_requires = []
142
143 # Nevow requires Twisted to setup, but doesn't declare that requirement in a way that enables
144 # setuptools to satisfy that requirement before Nevow's setup.py tried to "import twisted".
145 setup_requires.extend(['Twisted >= 2.4.0', 'setuptools_trial'])
146
147 # darcsver is needed only if you want "./setup.py darcsver" to write a new
148 # version stamp in src/allmydata/_version.py, with a version number derived from
149 # darcs history.
150 # http://pypi.python.org/pypi/darcsver
151 if 'darcsver' in sys.argv[1:]:
152     setup_requires.append('darcsver >= 1.1.5')
153
154 # setuptools_trial is needed only if you want "./setup.py trial" to execute the tests.
155 # http://pypi.python.org/pypi/setuptools_trial
156 if 'trial' in sys.argv[1:]:
157     # Cygwin requires the poll reactor to work at all.  Linux requires the poll reactor to avoid
158     # bug #402 (twisted bug #3218).  In general, the poll reactor is better than the select
159     # reactor, but it is not available on all platforms.  According to exarkun on IRC, it is
160     # available but buggy on some versions of Mac OS X, so just because you can install it
161     # doesn't mean we want to use it on every platform.
162     if sys.platform in ("linux2", "cygwin"):
163         if not [a for a in sys.argv if a.startswith("--reactor")]:
164             sys.argv.append("--reactor=poll")
165     setup_requires.append('setuptools_trial >= 0.2')
166
167     # Whenever we run the 'trial' command, make sure that the build_tahoe step
168     # is run as well.
169     trial_index = sys.argv.index('trial')
170     sys.argv.insert(trial_index, 'build_tahoe')
171
172 # setuptools_darcs is required to produce complete distributions (such as with
173 # "sdist" or "bdist_egg"), unless there is a PKG-INFO file present which shows
174 # that this is itself a source distribution.
175 # http://pypi.python.org/pypi/setuptools_darcs
176 if not os.path.exists('PKG-INFO'):
177     setup_requires.append('setuptools_darcs >= 1.1.0')
178
179 class ShowSupportLib(Command):
180     user_options = []
181     def initialize_options(self):
182         pass
183     def finalize_options(self):
184         pass
185     def run(self):
186         # TODO: --quiet suppresses the 'running show_supportlib' message.
187         # Find a way to do this all the time.
188         print supportlib # TODO windowsy
189
190 class ShowPythonPath(Command):
191     user_options = []
192     def initialize_options(self):
193         pass
194     def finalize_options(self):
195         pass
196     def run(self):
197         # TODO: --quiet suppresses the 'running show_supportlib' message.
198         # Find a way to do this all the time.
199         print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
200
201 class RunWithPythonPath(Command):
202     description = "Run a subcommand with PYTHONPATH set appropriately"
203
204     user_options = [ ("python", "p",
205                       "Treat command string as arguments to a python executable"),
206                      ("command=", "c", "Command to be run"),
207                      ("directory=", "d", "Directory to run the command in"),
208                      ]
209     boolean_options = ["python"]
210
211     def initialize_options(self):
212         self.command = None
213         self.python = False
214         self.directory = None
215     def finalize_options(self):
216         pass
217     def run(self):
218         oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
219         if oldpp == [""]:
220             # grr silly split() behavior
221             oldpp = []
222         os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
223
224         # We must require the command to be safe to split on
225         # whitespace, and have --python and --directory to make it
226         # easier to achieve this.
227
228         command = []
229         if self.python:
230             command.append(sys.executable)
231         if self.command:
232             command.extend(self.command.split())
233         if not command:
234             raise RuntimeError("The --command argument is mandatory")
235         if self.directory:
236             os.chdir(self.directory)
237         if self.verbose:
238             print "command =", " ".join(command)
239         rc = subprocess.call(command)
240         sys.exit(rc)
241
242 class CheckAutoDeps(Command):
243     user_options = []
244     def initialize_options(self):
245         pass
246     def finalize_options(self):
247         pass
248     def run(self):
249         import _auto_deps
250         _auto_deps.require_auto_deps()
251
252
253 class BuildTahoe(Command):
254     user_options = []
255     def initialize_options(self):
256         pass
257     def finalize_options(self):
258         pass
259     def run(self):
260         # chmod +x bin/tahoe
261         bin_tahoe = os.path.join("bin", "tahoe")
262         old_mode = stat.S_IMODE(os.stat(bin_tahoe)[stat.ST_MODE])
263         new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
264                                stat.S_IXGRP | stat.S_IRGRP |
265                                stat.S_IXOTH | stat.S_IROTH )
266         os.chmod(bin_tahoe, new_mode)
267
268         # On Windows, create the 'tahoe-script.py' file based on the 'tahoe'
269         # executable script under the 'bin' directory so that the tahoe.exe
270         # will work correctly.  The 'tahoe-script.py' file is exactly the same
271         # as the 'tahoe' script except that we need to update the she-bang
272         # line.  The tahoe.exe will be copied from the setuptools egg's cli.exe
273         # and this will work from a zip-safe and non-zip-safe setuptools egg.
274         if sys.platform == "win32":
275             setuptools_egg = require("setuptools")[0].location
276             if os.path.isfile(setuptools_egg):
277                 z = zipfile.ZipFile(setuptools_egg, 'r')
278                 for filename in z.namelist():
279                     if 'cli.exe' in filename:
280                         cli_exe = z.read(filename)
281             else:
282                 cli_exe = os.path.join(setuptools_egg, 'setuptools', 'cli.exe')
283             tahoe_exe = os.path.join("bin", "tahoe.exe")
284             if os.path.isfile(setuptools_egg):
285                 f = open(tahoe_exe, 'wb')
286                 f.write(cli_exe)
287                 f.close()
288             else:
289                 shutil.copy(cli_exe, tahoe_exe)
290             f = open(bin_tahoe, "r")
291             script_lines = f.readlines()
292             f.close()
293             script_lines[0] = "#!%s\n" % sys.executable
294             tahoe_script = os.path.join("bin", "tahoe-script.py")
295             f = open(tahoe_script, "w")
296             for line in script_lines:
297                 f.write(line)
298             f.close()
299
300         command = [sys.executable, "setup.py", "develop",
301             "--prefix=support"]
302         print "Command:", " ".join(command)
303         rc = subprocess.call(command)
304         if rc < 0:
305             print >>sys.stderr, "'setup.py develop' terminated by signal", -rc
306             sys.exit(1)
307         elif rc > 0:
308             print >>sys.stderr, "'setup.py develop' exited with rc", rc
309             sys.exit(rc)
310
311 class MySdist(sdist.sdist):
312     """ A hook in the sdist command so that we can determine whether this the
313     tarball should be 'SUMO' or not, i.e. whether or not to include the
314     external dependency tarballs. Note that we always include
315     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
316     is included as well.
317     """
318
319     user_options = sdist.sdist.user_options + \
320         [('sumo', 's',
321           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
322          ]
323     boolean_options = ['sumo']
324
325     def initialize_options(self):
326         sdist.sdist.initialize_options(self)
327         self.sumo = False
328
329     def make_distribution(self):
330         # add our extra files to the list just before building the
331         # tarball/zipfile. We override make_distribution() instead of run()
332         # because setuptools.command.sdist.run() does not lend itself to
333         # easy/robust subclassing (the code we need to add goes right smack
334         # in the middle of a 12-line method). If this were the distutils
335         # version, we'd override get_file_list().
336
337         if self.sumo:
338             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
339             # We assume that the user has fetched the tahoe-deps.tar.gz
340             # tarball and unpacked it already.
341             self.filelist.extend([os.path.join("tahoe-deps", fn)
342                                   for fn in os.listdir("tahoe-deps")])
343             # In addition, we want the tarball/zipfile to have -SUMO in the
344             # name, and the unpacked directory to have -SUMO too. The easiest
345             # way to do this is to patch self.distribution and override the
346             # get_fullname() method. (an alternative is to modify
347             # self.distribution.metadata.version, but that also affects the
348             # contents of PKG-INFO).
349             fullname = self.distribution.get_fullname()
350             def get_fullname():
351                 return fullname + "-SUMO"
352             self.distribution.get_fullname = get_fullname
353
354         return sdist.sdist.make_distribution(self)
355
356 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
357 # the _auto_deps.install_requires list, which is used in the call to setup()
358 # at the end of this file
359 from _auto_deps import install_requires
360
361 setup(name='allmydata-tahoe',
362       version=verstr,
363       description='secure, decentralized, fault-tolerant filesystem',
364       long_description=LONG_DESCRIPTION,
365       author='the allmydata.org Tahoe project',
366       author_email='tahoe-dev@allmydata.org',
367       url='http://allmydata.org/',
368       license='GNU GPL',
369       cmdclass={"show_supportlib": ShowSupportLib,
370                 "show_pythonpath": ShowPythonPath,
371                 "run_with_pythonpath": RunWithPythonPath,
372                 "check_auto_deps": CheckAutoDeps,
373                 "build_tahoe": BuildTahoe,
374                 "sdist": MySdist,
375                 },
376       package_dir = {'':'src'},
377       packages=find_packages("src"),
378       classifiers=trove_classifiers,
379       test_suite="allmydata.test",
380       install_requires=install_requires,
381       include_package_data=True,
382       setup_requires=setup_requires,
383       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
384       zip_safe=False, # We prefer unzipped for easier access.
385       )