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