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