]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
setup.py,Makefile: teat sdist --sumo about tahoe-deps/, use -SUMO suffix on tarballs...
[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, sys, subprocess
12
13 ##### sys.path management
14
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")
19 else:
20     supportlib = os.path.join("support", "lib", pyver, "site-packages")
21 supportlib = os.path.join(basedir, supportlib)
22
23 def add_tahoe_paths():
24     """Modify sys.path and PYTHONPATH to include Tahoe and supporting libraries
25
26     The first step towards building Tahoe is to run::
27
28       python setup.py build_tahoe
29
30     which is the equivalent of::
31
32       mkdir -p $(BASEDIR)/support/lib/python2.5/site-packages
33        (or cygpath equivalent)
34       setup.py develop --prefix=$(BASEDIR)/support
35
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.
40
41     We add this directory to os.environ['PYTHONPATH'], so that any child
42     processes we spawn will be able to use these packages.
43
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
52     a dependent library).
53
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).
59     """
60
61     extra_syspath_items = []
62     extra_pythonpath_items = []
63
64     extra_syspath_items.append(supportlib)
65     extra_pythonpath_items.append(supportlib)
66
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.
74
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))
79
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
83     # develop' command.
84     extra_syspath_items.append(os.path.join(basedir, "src"))
85
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
89     # trial"
90     oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
91     if oldpp == [""]:
92         # grr silly split() behavior
93         oldpp = []
94     extra_syspath_items = oldpp + extra_syspath_items
95
96     sys.path = extra_syspath_items + sys.path
97
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)
101
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.
105 add_tahoe_paths()
106
107 try:
108     from ez_setup import use_setuptools
109 except ImportError:
110     pass
111 else:
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.
114
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
118     # install_requires.
119     use_setuptools(download_delay=0, min_version="0.6c8")
120
121 from setuptools import Extension, find_packages, setup
122 from setuptools.command import sdist
123 from distutils.core import Command
124
125 # Make the dependency-version-requirement, which is used by the Makefile at
126 # build-time, also available to the app at runtime:
127 import shutil
128 try:
129     shutil.copyfile("_auto_deps.py", os.path.join("src", "allmydata", "_auto_deps.py"))
130 except EnvironmentError:
131     # Nevermind then -- perhaps it is already in place and in any case we can do
132     # without it.
133     pass
134
135 trove_classifiers=[
136     "Development Status :: 5 - Production/Stable",
137     "Environment :: Console",
138     "Environment :: Web Environment",
139     "License :: OSI Approved :: GNU General Public License (GPL)",
140     "License :: DFSG approved",
141     "License :: Other/Proprietary License",
142     "Intended Audience :: Developers",
143     "Intended Audience :: End Users/Desktop",
144     "Intended Audience :: System Administrators",
145     "Operating System :: Microsoft",
146     "Operating System :: Microsoft :: Windows",
147     "Operating System :: Unix",
148     "Operating System :: POSIX :: Linux",
149     "Operating System :: POSIX",
150     "Operating System :: MacOS :: MacOS X",
151     "Operating System :: Microsoft :: Windows :: Windows NT/2000",
152     "Operating System :: OS Independent",
153     "Natural Language :: English",
154     "Programming Language :: C",
155     "Programming Language :: Python",
156     "Topic :: Utilities",
157     "Topic :: System :: Systems Administration",
158     "Topic :: System :: Filesystems",
159     "Topic :: System :: Distributed Computing",
160     "Topic :: Software Development :: Libraries",
161     "Topic :: Communications :: Usenet News",
162     "Topic :: System :: Archiving :: Backup",
163     "Topic :: System :: Archiving :: Mirroring",
164     "Topic :: System :: Archiving",
165     ]
166
167
168 VERSIONFILE = "src/allmydata/_version.py"
169 verstr = "unknown"
170 try:
171     verstrline = open(VERSIONFILE, "rt").read()
172 except EnvironmentError:
173     pass # Okay, there is no version file.
174 else:
175     VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
176     mo = re.search(VSRE, verstrline, re.M)
177     if mo:
178         verstr = mo.group(1)
179     else:
180         print "unable to find version in %s" % (VERSIONFILE,)
181         raise RuntimeError("if %s.py exists, it is required to be well-formed" % (VERSIONFILE,))
182
183 LONG_DESCRIPTION=\
184 """Welcome to the Tahoe project, a secure, decentralized, fault-tolerant
185 filesystem.  All of the source code is available under a Free Software, Open
186 Source licence.
187
188 This filesystem is encrypted and spread over multiple peers in such a way that
189 it remains available even when some of the peers are unavailable,
190 malfunctioning, or malicious."""
191
192
193 # Default setup_requires are pyutil for the Windows installer builder(see
194 # misc/sub-ver.py) and Twisted for the tests.
195 #setup_requires = ['pyutil >= 1.3.16', 'Twisted >= 2.4.0']
196 setup_requires = []
197
198 # darcsver is needed only if you want "./setup.py darcsver" to write a new
199 # version stamp in src/allmydata/_version.py, with a version number derived from
200 # darcs history.
201 # http://pypi.python.org/pypi/darcsver
202 if 'darcsver' in sys.argv[1:]:
203     setup_requires.append('darcsver >= 1.1.5')
204
205 # setuptools_darcs is required to produce complete distributions (such as with
206 # "sdist" or "bdist_egg"), unless there is a PKG-INFO file present which shows
207 # that this is itself a source distribution.
208 # http://pypi.python.org/pypi/setuptools_darcs
209 if not os.path.exists('PKG-INFO'):
210     setup_requires.append('setuptools_darcs >= 1.1.0')
211
212 class ShowSupportLib(Command):
213     user_options = []
214     def initialize_options(self):
215         pass
216     def finalize_options(self):
217         pass
218     def run(self):
219         # TODO: --quiet suppresses the 'running show_supportlib' message.
220         # Find a way to do this all the time.
221         print supportlib # TODO windowsy
222
223 class ShowPythonPath(Command):
224     user_options = []
225     def initialize_options(self):
226         pass
227     def finalize_options(self):
228         pass
229     def run(self):
230         # TODO: --quiet suppresses the 'running show_supportlib' message.
231         # Find a way to do this all the time.
232         print "PYTHONPATH=%s" % os.environ["PYTHONPATH"]
233
234 class RunWithPythonPath(Command):
235     description = "Run a subcommand with PYTHONPATH set appropriately"
236
237     user_options = [ ("python", "p",
238                       "Treat command string as arguments to a python executable"),
239                      ("command=", "c", "Command to be run"),
240                      ("directory=", "d", "Directory to run the command in"),
241                      ]
242     boolean_options = ["python"]
243
244     def initialize_options(self):
245         self.command = None
246         self.python = False
247         self.directory = None
248     def finalize_options(self):
249         pass
250     def run(self):
251         # os.environ['PYTHONPATH'] is already set by add_tahoe_paths, so we
252         # just need to exec() their command. We must require the command to
253         # be safe to split on whitespace, and have --python and --directory
254         # to make it easier to achieve this.
255         command = []
256         if self.python:
257             command.append(sys.executable)
258         if self.command:
259             command.extend(self.command.split())
260         if not command:
261             raise RuntimeError("The --command argument is mandatory")
262         if self.directory:
263             os.chdir(self.directory)
264         if self.verbose:
265             print "command =", " ".join(command)
266         rc = subprocess.call(command)
267         sys.exit(rc)
268
269 class CheckAutoDeps(Command):
270     user_options = []
271     def initialize_options(self):
272         pass
273     def finalize_options(self):
274         pass
275     def run(self):
276         import _auto_deps
277         _auto_deps.require_auto_deps()
278
279
280 class BuildTahoe(Command):
281     user_options = []
282     def initialize_options(self):
283         pass
284     def finalize_options(self):
285         pass
286     def run(self):
287         # 'setup.py develop --prefix SUPPORT' will complain if SUPPORTLIB is
288         # not on PYTHONPATH, because it thinks you are installing to a place
289         # that will not be searched at runtime (which is true, except that we
290         # add SUPPORTLIB to PYTHONPATH to run tests, etc). So set up
291         # PYTHONPATH now, then spawn a 'setup.py develop' command. Also, we
292         # have to create the directory ourselves.
293         if not os.path.isdir(supportlib):
294             os.makedirs(supportlib)
295
296         command = [sys.executable, "setup.py", "develop", "--prefix", "support"]
297         if sys.platform == "linux2":
298             # workaround for tahoe #229 / setuptools #17, on debian
299             command.extend(["--site-dirs", "/var/lib/python-support/" + pyver])
300         print "Command:", " ".join(command)
301         rc = subprocess.call(command)
302         if rc < 0:
303             print >>sys.stderr, "'setup.py develop' terminated by signal", -rc
304             sys.exit(1)
305         elif rc > 0:
306             print >>sys.stderr, "'setup.py develop' exited with rc", rc
307             sys.exit(rc)
308
309 class Trial(Command):
310     # Unlike 'build' and 'bdist_egg', the 'trial' subcommand cannot be run in
311     # conjunction with other subcommands.
312
313     # The '-a' argument is split on whitespace and passed into trial. (the
314     # distutils parser does not give subcommands access to the rest of
315     # sys.argv, so unfortunately we cannot just do something like:
316     #   setup.py trial --reporter=text allmydata.test.test_util
317
318     # Examples:
319     #  setup.py trial    # run all tests
320     #  setup.py trial -a allmydata.test.test_util   # run some tests
321     #  setup.py trial -a '--reporter=text allmydata.test.test_util' #other args
322
323     description = "Run unit tests via trial"
324
325     user_options = [ ("args=", "a", "Argument string to pass to trial: setup.py trial -a allmydata.test.test_util"),
326                      ]
327     def initialize_options(self):
328         self.args = "allmydata"
329     def finalize_options(self):
330         pass
331
332     def run(self):
333         # make sure Twisted is available (for trial itself), and both the
334         # Tahoe source code and our dependent libraries are available (so
335         # that trial has some test code to work with)
336
337         from twisted.scripts import trial
338
339         args = self.args.strip().split()
340
341         # one wrinkle: we want to set the reactor here, because of bug #402
342         # (twisted bug #3218). We just jam in a "--reactor poll" at the start
343         # of the arglist. This does not permit the reactor to be overridden,
344         # unfortunately.
345         if sys.platform in ("linux2", "cygwin"):
346             # poll on linux2 to avoid #402 problems with select
347             # poll on cygwin since selectreactor runs out of fds
348             args = ["--reactor", "poll"] + args
349
350         # zooko also had os.environ["PYTHONUNBUFFERED"]="1" and
351         # args.append("--rterrors")
352
353         sys.argv = ["trial"] + args
354         if self.verbose > 1:
355             print "To run this test directly, use:"
356             print "PYTHONPATH=%s %s" % (os.environ["PYTHONPATH"],
357                                         " ".join(sys.argv))
358         else:
359             print "(run with -vv for trial command-line details)"
360         trial.run() # this does sys.exit
361         # NEVER REACHED
362
363 class MySdist(sdist.sdist):
364     """ A hook in the sdist command so that we can determine whether this the
365     tarball should be 'SUMO' or not, i.e. whether or not to include the
366     external dependency tarballs. Note that we always include
367     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
368     is included as well.
369     """
370
371     user_options = sdist.sdist.user_options + \
372         [('sumo', 's',
373           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
374          ]
375     boolean_options = ['sumo']
376
377     def initialize_options(self):
378         sdist.sdist.initialize_options(self)
379         self.sumo = False
380
381     def make_distribution(self):
382         # add our extra files to the list just before building the
383         # tarball/zipfile. We override make_distribution() instead of run()
384         # because setuptools.command.sdist.run() does not lend itself to
385         # easy/robust subclassing (the code we need to add goes right smack
386         # in the middle of a 12-line method). If this were the distutils
387         # version, we'd override get_file_list().
388
389         if self.sumo:
390             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
391             # We assume that the user has fetched the tahoe-deps.tar.gz
392             # tarball and unpacked it already.
393             self.filelist.extend([os.path.join("tahoe-deps", fn)
394                                   for fn in os.listdir("tahoe-deps")])
395             # In addition, we want the tarball/zipfile to have -SUMO in the
396             # name, and the unpacked directory to have -SUMO too. The easiest
397             # way to do this is to patch self.distribution and override the
398             # get_fullname() method. (an alternative is to modify
399             # self.distribution.metadata.version, but that also affects the
400             # contents of PKG-INFO).
401             fullname = self.distribution.get_fullname()
402             def get_fullname():
403                 return fullname + "-SUMO"
404             self.distribution.get_fullname = get_fullname
405
406         return sdist.sdist.make_distribution(self)
407
408 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
409 # the _auto_deps.install_requires list, which is used in the call to setup()
410 # at the end of this file
411 from _auto_deps import install_requires
412
413 setup(name='allmydata-tahoe',
414       version=verstr,
415       description='secure, decentralized, fault-tolerant filesystem',
416       long_description=LONG_DESCRIPTION,
417       author='the allmydata.org Tahoe project',
418       author_email='tahoe-dev@allmydata.org',
419       url='http://allmydata.org/',
420       license='GNU GPL',
421       cmdclass={"show_supportlib": ShowSupportLib,
422                 "show_pythonpath": ShowPythonPath,
423                 "run_with_pythonpath": RunWithPythonPath,
424                 "check_auto_deps": CheckAutoDeps,
425                 "build_tahoe": BuildTahoe,
426                 "trial": Trial,
427                 "sdist": MySdist,
428                 },
429       package_dir = {'':'src'},
430       packages=find_packages("src"),
431       classifiers=trove_classifiers,
432       test_suite="allmydata.test",
433       install_requires=install_requires,
434       include_package_data=True,
435       setup_requires=setup_requires,
436       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
437       zip_safe=False, # We prefer unzipped for easier access.
438       )