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