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