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