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