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