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