]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
setup: move the mock library from install_requires to tests_require (re: #1016)
[tahoe-lafs/tahoe-lafs.git] / setup.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # Tahoe-LAFS -- secure, distributed storage grid
5 #
6 # Copyright © 2008-2010 Allmydata, Inc.
7 #
8 # This file is part of Tahoe-LAFS.
9 #
10 # See the docs/about.html file for licensing information.
11
12 import glob, os, shutil, stat, subprocess, sys, zipfile, re
13
14 ##### sys.path management
15
16 def pylibdir(prefixdir):
17     pyver = "python%d.%d" % (sys.version_info[:2])
18     if sys.platform == "win32":
19         return os.path.join(prefixdir, "Lib", "site-packages")
20     else:
21         return os.path.join(prefixdir, "lib", pyver, "site-packages")
22
23 basedir = os.path.dirname(os.path.abspath(__file__))
24 supportlib = pylibdir(os.path.join(basedir, "support"))
25
26 # locate our version number
27
28 def read_version_py(infname):
29     try:
30         verstrline = open(infname, "rt").read()
31     except EnvironmentError:
32         return None
33     else:
34         VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
35         mo = re.search(VSRE, verstrline, re.M)
36         if mo:
37             return mo.group(1)
38
39 version = read_version_py("src/allmydata/_version.py")
40
41 egg = os.path.realpath(glob.glob('setuptools-*.egg')[0])
42 sys.path.insert(0, egg)
43 import setuptools; setuptools.bootstrap_install_from = egg
44
45 from setuptools import find_packages, setup
46 from setuptools.command import sdist
47 from setuptools import Command
48
49 # Make the dependency-version-requirement, which is used by the Makefile at
50 # build-time, also available to the app at runtime:
51 shutil.copyfile("_auto_deps.py",
52                 os.path.join("src", "allmydata", "_auto_deps.py"))
53
54 trove_classifiers=[
55     "Development Status :: 5 - Production/Stable",
56     "Environment :: Console",
57     "Environment :: Web Environment",
58     "License :: OSI Approved :: GNU General Public License (GPL)",
59     "License :: DFSG approved",
60     "License :: Other/Proprietary License",
61     "Intended Audience :: Developers",
62     "Intended Audience :: End Users/Desktop",
63     "Intended Audience :: System Administrators",
64     "Operating System :: Microsoft",
65     "Operating System :: Microsoft :: Windows",
66     "Operating System :: Microsoft :: Windows :: Windows NT/2000",
67     "Operating System :: Unix",
68     "Operating System :: POSIX :: Linux",
69     "Operating System :: POSIX",
70     "Operating System :: MacOS :: MacOS X",
71     "Operating System :: OS Independent",
72     "Natural Language :: English",
73     "Programming Language :: C",
74     "Programming Language :: Python",
75     "Programming Language :: Python :: 2",
76     "Programming Language :: Python :: 2.4",
77     "Programming Language :: Python :: 2.5",
78     "Programming Language :: Python :: 2.6",
79     "Topic :: Utilities",
80     "Topic :: System :: Systems Administration",
81     "Topic :: System :: Filesystems",
82     "Topic :: System :: Distributed Computing",
83     "Topic :: Software Development :: Libraries",
84     "Topic :: Communications :: Usenet News",
85     "Topic :: System :: Archiving :: Backup",
86     "Topic :: System :: Archiving :: Mirroring",
87     "Topic :: System :: Archiving",
88     ]
89
90
91 setup_requires = []
92
93 # The darcsver command from the darcsver plugin is needed to initialize the
94 # distribution's .version attribute correctly. (It does this either by
95 # examining darcs history, or if that fails by reading the
96 # src/allmydata/_version.py file). darcsver will also write a new version
97 # stamp in src/allmydata/_version.py, with a version number derived from
98 # darcs history. Note that the setup.cfg file has an "[aliases]" section
99 # which enumerates commands that you might run and specifies that it will run
100 # darcsver before each one. If you add different commands (or if I forgot
101 # some that are already in use), you may need to add it to setup.cfg and
102 # configure it to run darcsver before your command, if you want the version
103 # number to be correct when that command runs.
104 # http://pypi.python.org/pypi/darcsver
105 setup_requires.append('darcsver >= 1.2.0')
106
107 # Nevow requires Twisted to setup, but doesn't declare that requirement in a
108 # way that enables setuptools to satisfy that requirement before Nevow's
109 # setup.py tried to "import twisted". Fortunately we require setuptools_trial
110 # to setup and setuptools_trial requires Twisted to install, so hopefully
111 # everything will work out until the Nevow issue is fixed:
112 # http://divmod.org/trac/ticket/2629 setuptools_trial is needed if you want
113 # "./setup.py trial" or "./setup.py test" to execute the tests (and in order
114 # to make sure Twisted is installed early enough -- see the paragraph above).
115 # http://pypi.python.org/pypi/setuptools_trial
116 setup_requires.extend(['setuptools_trial >= 0.5'])
117
118 # setuptools_darcs is required to produce complete distributions (such as
119 # with "sdist" or "bdist_egg") (unless there is a PKG-INFO file present which
120 # shows that this is itself a source distribution). For simplicity, and
121 # because there is some unknown error with setuptools_darcs when building and
122 # testing tahoe all in one python command on some platforms, we always add it
123 # to setup_requires. http://pypi.python.org/pypi/setuptools_darcs
124 setup_requires.append('setuptools_darcs >= 1.1.0')
125
126 # trialcoverage is required if you want the "trial" unit test runner to have a
127 # "--reporter=bwverbose-coverage" option which produces code-coverage results.
128 # The required version is 0.3.3, because that is the latest version that only
129 # depends on a version of pycoverage for which binary packages are available.
130 if "--reporter=bwverbose-coverage" in sys.argv:
131     setup_requires.append('trialcoverage >= 0.3.3')
132
133 # stdeb is required to produce Debian files with the "sdist_dsc" command.
134 if "sdist_dsc" in sys.argv:
135     setup_requires.append('stdeb >= 0.3')
136
137 tests_require=[
138     # Mock - Mocking and Testing Library
139     # http://www.voidspace.org.uk/python/mock/
140     "mock",
141     ]
142
143 class ShowSupportLib(Command):
144     user_options = []
145     def initialize_options(self):
146         pass
147     def finalize_options(self):
148         pass
149     def run(self):
150         # TODO: --quiet suppresses the 'running show_supportlib' message.
151         # Find a way to do this all the time.
152         print supportlib # TODO windowsy
153
154 class ShowPythonPath(Command):
155     user_options = []
156     def initialize_options(self):
157         pass
158     def finalize_options(self):
159         pass
160     def run(self):
161         # TODO: --quiet suppresses the 'running show_supportlib' message.
162         # Find a way to do this all the time.
163         print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
164
165 class RunWithPythonPath(Command):
166     description = "Run a subcommand with PYTHONPATH set appropriately"
167
168     user_options = [ ("python", "p",
169                       "Treat command string as arguments to a python executable"),
170                      ("command=", "c", "Command to be run"),
171                      ("directory=", "d", "Directory to run the command in"),
172                      ]
173     boolean_options = ["python"]
174
175     def initialize_options(self):
176         self.command = None
177         self.python = False
178         self.directory = None
179     def finalize_options(self):
180         pass
181     def run(self):
182         oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
183         if oldpp == [""]:
184             # grr silly split() behavior
185             oldpp = []
186         os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
187
188         # We must require the command to be safe to split on
189         # whitespace, and have --python and --directory to make it
190         # easier to achieve this.
191
192         command = []
193         if self.python:
194             command.append(sys.executable)
195         if self.command:
196             command.extend(self.command.split())
197         if not command:
198             raise RuntimeError("The --command argument is mandatory")
199         if self.directory:
200             os.chdir(self.directory)
201         if self.verbose:
202             print "command =", " ".join(command)
203         rc = subprocess.call(command)
204         sys.exit(rc)
205
206 class TestMacDiskImage(Command):
207     user_options = []
208     def initialize_options(self):
209         pass
210     def finalize_options(self):
211         pass
212     def run(self):
213         import sys
214         sys.path.append(os.path.join('misc', 'build_helpers'))
215         import test_mac_diskimage
216         return test_mac_diskimage.test_mac_diskimage('Allmydata', version=self.distribution.metadata.version)
217
218 class CheckAutoDeps(Command):
219     user_options = []
220     def initialize_options(self):
221         pass
222     def finalize_options(self):
223         pass
224     def run(self):
225         import _auto_deps
226         _auto_deps.require_auto_deps()
227
228
229 class MakeExecutable(Command):
230     user_options = []
231     def initialize_options(self):
232         pass
233     def finalize_options(self):
234         pass
235     def run(self):
236         bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
237
238         # Create the 'tahoe-script.py' file under the 'bin' directory. The
239         # 'tahoe-script.py' file is exactly the same as the
240         # 'tahoe-script.template' script except that the shebang line is
241         # rewritten to use our sys.executable for the interpreter. On
242         # Windows, create a tahoe.exe will execute it. On non-Windows, make a
243         # symlink to it from 'tahoe'. The tahoe.exe will be copied from the
244         # setuptools egg's cli.exe and this will work from a zip-safe and
245         # non-zip-safe setuptools egg.
246         f = open(bin_tahoe_template, "rU")
247         script_lines = f.readlines()
248         f.close()
249         script_lines[0] = "#!%s\n" % sys.executable
250         tahoe_script = os.path.join("bin", "tahoe-script.py")
251         f = open(tahoe_script, "w")
252         for line in script_lines:
253             f.write(line)
254         f.close()
255         if sys.platform == "win32":
256             from pkg_resources import require
257             setuptools_egg = require("setuptools")[0].location
258             if os.path.isfile(setuptools_egg):
259                 z = zipfile.ZipFile(setuptools_egg, 'r')
260                 for filename in z.namelist():
261                     if 'cli.exe' in filename:
262                         cli_exe = z.read(filename)
263             else:
264                 cli_exe = os.path.join(setuptools_egg, 'setuptools', 'cli.exe')
265             tahoe_exe = os.path.join("bin", "tahoe.exe")
266             if os.path.isfile(setuptools_egg):
267                 f = open(tahoe_exe, 'wb')
268                 f.write(cli_exe)
269                 f.close()
270             else:
271                 shutil.copy(cli_exe, tahoe_exe)
272         else:
273             try:
274                 os.remove(os.path.join('bin', 'tahoe'))
275             except:
276                 # okay, probably it was already gone
277                 pass
278             os.symlink('tahoe-script.py', os.path.join('bin', 'tahoe'))
279
280         # chmod +x bin/tahoe-script.py
281         old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE])
282         new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
283                                stat.S_IXGRP | stat.S_IRGRP |
284                                stat.S_IXOTH | stat.S_IROTH )
285         os.chmod(tahoe_script, new_mode)
286
287 class MySdist(sdist.sdist):
288     """ A hook in the sdist command so that we can determine whether this the
289     tarball should be 'SUMO' or not, i.e. whether or not to include the
290     external dependency tarballs. Note that we always include
291     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
292     is included as well.
293     """
294
295     user_options = sdist.sdist.user_options + \
296         [('sumo', 's',
297           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
298          ]
299     boolean_options = ['sumo']
300
301     def initialize_options(self):
302         sdist.sdist.initialize_options(self)
303         self.sumo = False
304
305     def make_distribution(self):
306         # add our extra files to the list just before building the
307         # tarball/zipfile. We override make_distribution() instead of run()
308         # because setuptools.command.sdist.run() does not lend itself to
309         # easy/robust subclassing (the code we need to add goes right smack
310         # in the middle of a 12-line method). If this were the distutils
311         # version, we'd override get_file_list().
312
313         if self.sumo:
314             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
315             # We assume that the user has fetched the tahoe-deps.tar.gz
316             # tarball and unpacked it already.
317             self.filelist.extend([os.path.join("tahoe-deps", fn)
318                                   for fn in os.listdir("tahoe-deps")])
319             # In addition, we want the tarball/zipfile to have -SUMO in the
320             # name, and the unpacked directory to have -SUMO too. The easiest
321             # way to do this is to patch self.distribution and override the
322             # get_fullname() method. (an alternative is to modify
323             # self.distribution.metadata.version, but that also affects the
324             # contents of PKG-INFO).
325             fullname = self.distribution.get_fullname()
326             def get_fullname():
327                 return fullname + "-SUMO"
328             self.distribution.get_fullname = get_fullname
329
330         return sdist.sdist.make_distribution(self)
331
332 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
333 # the _auto_deps.install_requires list, which is used in the call to setup()
334 # below.
335 from _auto_deps import install_requires
336
337 APPNAME='allmydata-tahoe'
338 APPNAMEFILE = os.path.join('src', 'allmydata', '_appname.py')
339 APPNAMEFILESTR = "__appname__ = '%s'" % (APPNAME,)
340 try:
341     curappnamefilestr = open(APPNAMEFILE, 'rU').read()
342 except EnvironmentError:
343     # No file, or unreadable or something, okay then let's try to write one.
344     open(APPNAMEFILE, "w").write(APPNAMEFILESTR)
345 else:
346     if curappnamefilestr.strip() != APPNAMEFILESTR:
347         print "Error -- this setup.py file is configured with the 'application name' to be '%s', but there is already a file in place in '%s' which contains the contents '%s'.  If the file is wrong, please remove it and setup.py will regenerate it and write '%s' into it." % (APPNAME, APPNAMEFILE, curappnamefilestr, APPNAMEFILESTR)
348         sys.exit(-1)
349
350 setup_args = {}
351 if version:
352     setup_args["version"] = version
353
354 setup(name=APPNAME,
355       description='secure, decentralized, fault-tolerant filesystem',
356       long_description=open('README.txt', 'rU').read(),
357       author='the Tahoe-LAFS project',
358       author_email='tahoe-dev@allmydata.org',
359       url='http://tahoe-lafs.org/',
360       license='GNU GPL', # see README.txt -- there is an alternative licence
361       cmdclass={"show_supportlib": ShowSupportLib,
362                 "show_pythonpath": ShowPythonPath,
363                 "run_with_pythonpath": RunWithPythonPath,
364                 "check_auto_deps": CheckAutoDeps,
365                 "test_mac_diskimage": TestMacDiskImage,
366                 "make_executable": MakeExecutable,
367                 "sdist": MySdist,
368                 },
369       package_dir = {'':'src'},
370       packages=find_packages("src"),
371       classifiers=trove_classifiers,
372       test_suite="allmydata.test",
373       install_requires=install_requires,
374       tests_require=tests_require,
375       include_package_data=True,
376       setup_requires=setup_requires,
377       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
378       zip_safe=False, # We prefer unzipped for easier access.
379       **setup_args
380       )