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