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