]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
setup: if any of "build", "develop", or "test" appear in the sys.argv then that means...
[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") or arg.startswith("build") or arg.startswith("test"): # argh! horrible kludge to workaround setuptools #17
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     "Programming Language :: Python :: 2.6",
105     "Topic :: Utilities",
106     "Topic :: System :: Systems Administration",
107     "Topic :: System :: Filesystems",
108     "Topic :: System :: Distributed Computing",
109     "Topic :: Software Development :: Libraries",
110     "Topic :: Communications :: Usenet News",
111     "Topic :: System :: Archiving :: Backup",
112     "Topic :: System :: Archiving :: Mirroring",
113     "Topic :: System :: Archiving",
114     ]
115
116
117 VERSIONFILE = "src/allmydata/_version.py"
118 verstr = "unknown"
119 try:
120     verstrline = open(VERSIONFILE, "rt").read()
121 except EnvironmentError:
122     pass # Okay, there is no version file.
123 else:
124     VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
125     mo = re.search(VSRE, verstrline, re.M)
126     if mo:
127         verstr = mo.group(1)
128     else:
129         print "unable to find version in %s" % (VERSIONFILE,)
130         raise RuntimeError("if %s.py exists, it is required to be well-formed" % (VERSIONFILE,))
131
132 LONG_DESCRIPTION=\
133 """Welcome to the Tahoe project, a secure, decentralized, fault-tolerant
134 filesystem.  All of the source code is available under a Free Software, Open
135 Source licence.
136
137 This filesystem is encrypted and spread over multiple peers in such a way that
138 it remains available even when some of the peers are unavailable,
139 malfunctioning, or malicious."""
140
141
142 setup_requires = []
143
144 # Nevow requires Twisted to setup, but doesn't declare that requirement in a way that enables
145 # setuptools to satisfy that requirement before Nevow's setup.py tried to "import twisted".
146 # Fortunately we require setuptools_trial to setup and setuptools_trial requires Twisted to
147 # install, so hopefully everything will work out until the Nevow issue is fixed:
148 # http://divmod.org/trac/ticket/2629
149 # setuptools_trial is needed if you want "./setup.py trial" or "./setup.py test" to execute the
150 # tests (and in order to make sure Twisted is installed early enough -- see the paragraph
151 # above).
152 # http://pypi.python.org/pypi/setuptools_trial
153 setup_requires.extend(['setuptools_trial'])
154
155 # darcsver is needed if you want "./setup.py darcsver" to write a new version stamp in
156 # src/allmydata/_version.py, with a version number derived from darcs history.
157 # http://pypi.python.org/pypi/darcsver
158 setup_requires.append('darcsver >= 1.1.5')
159
160 if 'trial' in sys.argv[1:] or 'test' in sys.argv[1:]:
161     # Cygwin requires the poll reactor to work at all.  Linux requires the poll reactor to avoid
162     # bug #402 (twisted bug #3218).  In general, the poll reactor is better than the select
163     # reactor, but it is not available on all platforms.  According to exarkun on IRC, it is
164     # available but buggy on some versions of Mac OS X, so just because you can install it
165     # doesn't mean we want to use it on every platform.
166     if sys.platform in ("linux2", "cygwin"):
167         if not [a for a in sys.argv if a.startswith("--reactor")]:
168             sys.argv.append("--reactor=poll")
169
170 # setuptools_darcs is required to produce complete distributions (such as with
171 # "sdist" or "bdist_egg"), unless there is a PKG-INFO file present which shows
172 # that this is itself a source distribution.
173 # http://pypi.python.org/pypi/setuptools_darcs
174 if not os.path.exists('PKG-INFO'):
175     setup_requires.append('setuptools_darcs >= 1.1.0')
176
177 class ShowSupportLib(Command):
178     user_options = []
179     def initialize_options(self):
180         pass
181     def finalize_options(self):
182         pass
183     def run(self):
184         # TODO: --quiet suppresses the 'running show_supportlib' message.
185         # Find a way to do this all the time.
186         print supportlib # TODO windowsy
187
188 class ShowPythonPath(Command):
189     user_options = []
190     def initialize_options(self):
191         pass
192     def finalize_options(self):
193         pass
194     def run(self):
195         # TODO: --quiet suppresses the 'running show_supportlib' message.
196         # Find a way to do this all the time.
197         print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
198
199 class RunWithPythonPath(Command):
200     description = "Run a subcommand with PYTHONPATH set appropriately"
201
202     user_options = [ ("python", "p",
203                       "Treat command string as arguments to a python executable"),
204                      ("command=", "c", "Command to be run"),
205                      ("directory=", "d", "Directory to run the command in"),
206                      ]
207     boolean_options = ["python"]
208
209     def initialize_options(self):
210         self.command = None
211         self.python = False
212         self.directory = None
213     def finalize_options(self):
214         pass
215     def run(self):
216         oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
217         if oldpp == [""]:
218             # grr silly split() behavior
219             oldpp = []
220         os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
221
222         # We must require the command to be safe to split on
223         # whitespace, and have --python and --directory to make it
224         # easier to achieve this.
225
226         command = []
227         if self.python:
228             command.append(sys.executable)
229         if self.command:
230             command.extend(self.command.split())
231         if not command:
232             raise RuntimeError("The --command argument is mandatory")
233         if self.directory:
234             os.chdir(self.directory)
235         if self.verbose:
236             print "command =", " ".join(command)
237         rc = subprocess.call(command)
238         sys.exit(rc)
239
240 class CheckAutoDeps(Command):
241     user_options = []
242     def initialize_options(self):
243         pass
244     def finalize_options(self):
245         pass
246     def run(self):
247         import _auto_deps
248         _auto_deps.require_auto_deps()
249
250
251 class MakeExecutable(Command):
252     user_options = []
253     def initialize_options(self):
254         pass
255     def finalize_options(self):
256         pass
257     def run(self):
258         bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
259
260         # Create the 'tahoe-script.py' file under the 'bin' directory. The 'tahoe-script.py'
261         # file is exactly the same as the 'tahoe-script.template' script except that the shebang
262         # line is rewritten to use our sys.executable for the interpreter. On Windows, create a
263         # tahoe.exe will execute it.  On non-Windows, make a symlink to it from 'tahoe'.  The
264         # tahoe.exe will be copied from the setuptools egg's cli.exe and this will work from a
265         # zip-safe and non-zip-safe setuptools egg.
266         f = open(bin_tahoe_template, "rU")
267         script_lines = f.readlines()
268         f.close()
269         script_lines[0] = "#!%s\n" % sys.executable
270         tahoe_script = os.path.join("bin", "tahoe-script.py")
271         f = open(tahoe_script, "w")
272         for line in script_lines:
273             f.write(line)
274         f.close()
275         if sys.platform == "win32":
276             setuptools_egg = require("setuptools")[0].location
277             if os.path.isfile(setuptools_egg):
278                 z = zipfile.ZipFile(setuptools_egg, 'r')
279                 for filename in z.namelist():
280                     if 'cli.exe' in filename:
281                         cli_exe = z.read(filename)
282             else:
283                 cli_exe = os.path.join(setuptools_egg, 'setuptools', 'cli.exe')
284             tahoe_exe = os.path.join("bin", "tahoe.exe")
285             if os.path.isfile(setuptools_egg):
286                 f = open(tahoe_exe, 'wb')
287                 f.write(cli_exe)
288                 f.close()
289             else:
290                 shutil.copy(cli_exe, tahoe_exe)
291         else:
292             try:
293                 os.remove(os.path.join('bin', 'tahoe'))
294             except:
295                 # okay, probably it was already gone
296                 pass
297             os.symlink('tahoe-script.py', os.path.join('bin', 'tahoe'))
298
299         # chmod +x bin/tahoe-script.py
300         old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE])
301         new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
302                                stat.S_IXGRP | stat.S_IRGRP |
303                                stat.S_IXOTH | stat.S_IROTH )
304         os.chmod(tahoe_script, new_mode)
305
306 class MySdist(sdist.sdist):
307     """ A hook in the sdist command so that we can determine whether this the
308     tarball should be 'SUMO' or not, i.e. whether or not to include the
309     external dependency tarballs. Note that we always include
310     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
311     is included as well.
312     """
313
314     user_options = sdist.sdist.user_options + \
315         [('sumo', 's',
316           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
317          ]
318     boolean_options = ['sumo']
319
320     def initialize_options(self):
321         sdist.sdist.initialize_options(self)
322         self.sumo = False
323
324     def make_distribution(self):
325         # add our extra files to the list just before building the
326         # tarball/zipfile. We override make_distribution() instead of run()
327         # because setuptools.command.sdist.run() does not lend itself to
328         # easy/robust subclassing (the code we need to add goes right smack
329         # in the middle of a 12-line method). If this were the distutils
330         # version, we'd override get_file_list().
331
332         if self.sumo:
333             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
334             # We assume that the user has fetched the tahoe-deps.tar.gz
335             # tarball and unpacked it already.
336             self.filelist.extend([os.path.join("tahoe-deps", fn)
337                                   for fn in os.listdir("tahoe-deps")])
338             # In addition, we want the tarball/zipfile to have -SUMO in the
339             # name, and the unpacked directory to have -SUMO too. The easiest
340             # way to do this is to patch self.distribution and override the
341             # get_fullname() method. (an alternative is to modify
342             # self.distribution.metadata.version, but that also affects the
343             # contents of PKG-INFO).
344             fullname = self.distribution.get_fullname()
345             def get_fullname():
346                 return fullname + "-SUMO"
347             self.distribution.get_fullname = get_fullname
348
349         return sdist.sdist.make_distribution(self)
350
351 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
352 # the _auto_deps.install_requires list, which is used in the call to setup()
353 # at the end of this file
354 from _auto_deps import install_requires
355
356 setup(name='allmydata-tahoe',
357       version=verstr,
358       description='secure, decentralized, fault-tolerant filesystem',
359       long_description=LONG_DESCRIPTION,
360       author='the allmydata.org Tahoe project',
361       author_email='tahoe-dev@allmydata.org',
362       url='http://allmydata.org/',
363       license='GNU GPL',
364       cmdclass={"show_supportlib": ShowSupportLib,
365                 "show_pythonpath": ShowPythonPath,
366                 "run_with_pythonpath": RunWithPythonPath,
367                 "check_auto_deps": CheckAutoDeps,
368                 "make_executable": MakeExecutable,
369                 "sdist": MySdist,
370                 },
371       package_dir = {'':'src'},
372       packages=find_packages("src"),
373       classifiers=trove_classifiers,
374       test_suite="allmydata.test",
375       install_requires=install_requires,
376       include_package_data=True,
377       setup_requires=setup_requires,
378       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
379       zip_safe=False, # We prefer unzipped for easier access.
380       )