]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
fix bin/tahoe executable for Windows
[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 for i in range(len(sys.argv)):
26     arg = sys.argv[i]
27     if arg == "build_tahoe":
28         del sys.argv[i]
29         sys.argv.extend(["develop", "--prefix=support", "--script-dir=support/bin"])
30
31 for i in range(len(sys.argv)):
32     arg = sys.argv[i]
33     prefixdir = None
34     if arg.startswith("--prefix="):
35         prefixdir = arg[len("--prefix="):]
36     if arg == "--prefix":
37         if len(sys.argv) > i+1:
38             prefixdir = sys.argv[i+1]
39
40     if prefixdir:
41         libdir = pylibdir(prefixdir)
42         try:
43             os.makedirs(libdir)
44         except EnvironmentError, le:
45             # Okay, maybe the dir was already there.
46             pass
47         sys.path.append(libdir)
48         pp = os.environ.get('PYTHONPATH','').split(os.pathsep)
49         pp.append(libdir)
50         os.environ['PYTHONPATH'] = os.pathsep.join(pp)
51
52     if arg.startswith("build"):
53         # chmod +x bin/tahoe
54         bin_tahoe = os.path.join("bin", "tahoe")
55         old_mode = stat.S_IMODE(os.stat(bin_tahoe)[stat.ST_MODE])
56         new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
57                                stat.S_IXGRP | stat.S_IRGRP |
58                                stat.S_IXOTH | stat.S_IROTH )
59         os.chmod(bin_tahoe, new_mode)
60
61     if arg.startswith("install") or arg.startswith("develop"):
62         if sys.platform == "linux2":
63             # workaround for tahoe #229 / setuptools #17, on debian
64             sys.argv.extend(["--site-dirs", "/var/lib/python-support/python%d.%d" % (sys.version_info[:2])])
65         elif sys.platform == "darwin":
66             # this probably only applies to leopard 10.5, possibly only 10.5.5
67             sd = "/System/Library/Frameworks/Python.framework/Versions/%d.%d/Extras/lib/python" % (sys.version_info[:2])
68             sys.argv.extend(["--site-dirs", sd])
69
70 try:
71     from ez_setup import use_setuptools
72 except ImportError:
73     pass
74 else:
75     # This invokes our own customized version of ez_setup.py to make sure that
76     # setuptools >= v0.6c8 (a.k.a. v0.6-final) is installed.
77
78     # setuptools < v0.6c8 doesn't handle eggs which get installed into the CWD
79     # as a result of being transitively depended on in a setup_requires, but
80     # then are needed for the installed code to run, i.e. in an
81     # install_requires.
82     use_setuptools(download_delay=0, min_version="0.6c10dev")
83
84 from setuptools import find_packages, setup
85 from setuptools.command import sdist
86 from distutils.core import Command
87 from pkg_resources import require
88
89 import pkg_resources
90 pkg_resources.require('setuptools_trial')
91 from setuptools_trial.setuptools_trial import TrialTest
92
93 # Make the dependency-version-requirement, which is used by the Makefile at
94 # build-time, also available to the app at runtime:
95 import shutil
96 shutil.copyfile("_auto_deps.py", os.path.join("src", "allmydata", "_auto_deps.py"))
97
98 trove_classifiers=[
99     "Development Status :: 5 - Production/Stable",
100     "Environment :: Console",
101     "Environment :: Web Environment",
102     "License :: OSI Approved :: GNU General Public License (GPL)",
103     "License :: DFSG approved",
104     "License :: Other/Proprietary License",
105     "Intended Audience :: Developers",
106     "Intended Audience :: End Users/Desktop",
107     "Intended Audience :: System Administrators",
108     "Operating System :: Microsoft",
109     "Operating System :: Microsoft :: Windows",
110     "Operating System :: Microsoft :: Windows :: Windows NT/2000",
111     "Operating System :: Unix",
112     "Operating System :: POSIX :: Linux",
113     "Operating System :: POSIX",
114     "Operating System :: MacOS :: MacOS X",
115     "Operating System :: OS Independent",
116     "Natural Language :: English",
117     "Programming Language :: C",
118     "Programming Language :: Python",
119     "Programming Language :: Python :: 2",
120     "Programming Language :: Python :: 2.4",
121     "Programming Language :: Python :: 2.5",
122     "Topic :: Utilities",
123     "Topic :: System :: Systems Administration",
124     "Topic :: System :: Filesystems",
125     "Topic :: System :: Distributed Computing",
126     "Topic :: Software Development :: Libraries",
127     "Topic :: Communications :: Usenet News",
128     "Topic :: System :: Archiving :: Backup",
129     "Topic :: System :: Archiving :: Mirroring",
130     "Topic :: System :: Archiving",
131     ]
132
133
134 VERSIONFILE = "src/allmydata/_version.py"
135 verstr = "unknown"
136 try:
137     verstrline = open(VERSIONFILE, "rt").read()
138 except EnvironmentError:
139     pass # Okay, there is no version file.
140 else:
141     VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
142     mo = re.search(VSRE, verstrline, re.M)
143     if mo:
144         verstr = mo.group(1)
145     else:
146         print "unable to find version in %s" % (VERSIONFILE,)
147         raise RuntimeError("if %s.py exists, it is required to be well-formed" % (VERSIONFILE,))
148
149 LONG_DESCRIPTION=\
150 """Welcome to the Tahoe project, a secure, decentralized, fault-tolerant
151 filesystem.  All of the source code is available under a Free Software, Open
152 Source licence.
153
154 This filesystem is encrypted and spread over multiple peers in such a way that
155 it remains available even when some of the peers are unavailable,
156 malfunctioning, or malicious."""
157
158
159 setup_requires = []
160
161 # Nevow requires Twisted to setup, but doesn't declare that requirement in a way that enables
162 # setuptools to satisfy that requirement before Nevow's setup.py tried to "import twisted".
163 setup_requires.extend(['Twisted >= 2.4.0', 'setuptools_trial'])
164
165 # darcsver is needed only if you want "./setup.py darcsver" to write a new
166 # version stamp in src/allmydata/_version.py, with a version number derived from
167 # darcs history.
168 # http://pypi.python.org/pypi/darcsver
169 if 'darcsver' in sys.argv[1:]:
170     setup_requires.append('darcsver >= 1.1.5')
171
172 # setuptools_trial is needed only if you want "./setup.py trial" to execute the tests.
173 # http://pypi.python.org/pypi/setuptools_trial
174 if 'trial' in sys.argv[1:]:
175     setup_requires.append('setuptools_trial >= 0.2')
176
177 # setuptools_darcs is required to produce complete distributions (such as with
178 # "sdist" or "bdist_egg"), unless there is a PKG-INFO file present which shows
179 # that this is itself a source distribution.
180 # http://pypi.python.org/pypi/setuptools_darcs
181 if not os.path.exists('PKG-INFO'):
182     setup_requires.append('setuptools_darcs >= 1.1.0')
183
184 class ShowSupportLib(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 supportlib # TODO windowsy
194
195 class ShowPythonPath(Command):
196     user_options = []
197     def initialize_options(self):
198         pass
199     def finalize_options(self):
200         pass
201     def run(self):
202         # TODO: --quiet suppresses the 'running show_supportlib' message.
203         # Find a way to do this all the time.
204         print "PYTHONPATH=%s" % os.environ["PYTHONPATH"]
205
206 class RunWithPythonPath(Command):
207     description = "Run a subcommand with PYTHONPATH set appropriately"
208
209     user_options = [ ("python", "p",
210                       "Treat command string as arguments to a python executable"),
211                      ("command=", "c", "Command to be run"),
212                      ("directory=", "d", "Directory to run the command in"),
213                      ]
214     boolean_options = ["python"]
215
216     def initialize_options(self):
217         self.command = None
218         self.python = False
219         self.directory = None
220     def finalize_options(self):
221         pass
222     def run(self):
223         # os.environ['PYTHONPATH'] is already set by add_tahoe_paths, so we
224         # just need to exec() their command. We must require the command to
225         # be safe to split on whitespace, and have --python and --directory
226         # to make it easier to achieve this.
227         command = []
228         if self.python:
229             command.append(sys.executable)
230         if self.command:
231             command.extend(self.command.split())
232         if not command:
233             raise RuntimeError("The --command argument is mandatory")
234         if self.directory:
235             os.chdir(self.directory)
236         if self.verbose:
237             print "command =", " ".join(command)
238         rc = subprocess.call(command)
239         sys.exit(rc)
240
241 class CheckAutoDeps(Command):
242     user_options = []
243     def initialize_options(self):
244         pass
245     def finalize_options(self):
246         pass
247     def run(self):
248         import _auto_deps
249         _auto_deps.require_auto_deps()
250
251
252 class BuildTahoe(Command):
253     user_options = []
254     def initialize_options(self):
255         pass
256     def finalize_options(self):
257         pass
258     def run(self):
259         # On Windows, create the 'tahoe-script.py' file based on the 'tahoe'
260         # executable script under the 'bin' directory so that the tahoe.exe
261         # will work correctly.  The 'tahoe-script.py' file is exactly the same
262         # as the 'tahoe' script except that we need to update the she-bang
263         # line.  The tahoe.exe will be copied from the setuptools egg's cli.exe
264         # and this will work from a zip-safe and non-zip-safe setuptools egg.
265         if sys.platform == "win32":
266             setuptools_egg = require("setuptools")[0].location
267             if os.path.isfile(setuptools_egg):
268                 z = zipfile.ZipFile(setuptools_egg, 'r')
269                 for filename in z.namelist():
270                     if 'cli.exe' in filename:
271                         cli_exe = z.read(filename)
272             else:
273                 cli_exe = os.path.join(setuptools_egg, 'setuptools', 'cli.exe')
274             tahoe_exe = os.path.join("bin", "tahoe.exe")
275             if os.path.isfile(setuptools_egg):
276                 f = open(tahoe_exe, 'wb')
277                 f.write(cli_exe)
278                 f.close()
279             else:
280                 shutil.copy(cli_exe, tahoe_exe)
281             bin_tahoe = os.path.join("bin", "tahoe")
282             f = open(bin_tahoe, "r")
283             script_lines = f.readlines()
284             f.close()
285             script_lines[0] = "#!%s\n" % sys.executable
286             tahoe_script = os.path.join("bin", "tahoe-script.py")
287             f = open(tahoe_script, "w")
288             for line in script_lines:
289                 f.write(line)
290             f.close()
291
292         command = [sys.executable, "setup.py", "develop", "--prefix", "support"]
293         print "Command:", " ".join(command)
294         rc = subprocess.call(command)
295         if rc < 0:
296             print >>sys.stderr, "'setup.py develop' terminated by signal", -rc
297             sys.exit(1)
298         elif rc > 0:
299             print >>sys.stderr, "'setup.py develop' exited with rc", rc
300             sys.exit(rc)
301
302 class Trial(TrialTest):
303     # Custom sub-class of the TrialTest class from the setuptools_trial
304     # plugin so that we can ensure certain options are set by default.
305     #
306     # Examples:
307     #  setup.py trial    # run all tests
308     #  setup.py trial -a allmydata.test.test_util   # run some tests
309     #  setup.py trial -a '--reporter=text allmydata.test.test_util' #other args
310
311
312     def initialize_options(self):
313         TrialTest.initialize_options(self)
314
315         # We want to set the reactor to 'poll', because of bug #402
316         # (twisted bug #3218).
317         if sys.platform in ("linux2", "cygwin"):
318             # poll on linux2 to avoid #402 problems with select
319             # poll on cygwin since selectreactor runs out of fds
320             self.reactor = "poll"
321
322
323 class MySdist(sdist.sdist):
324     """ A hook in the sdist command so that we can determine whether this the
325     tarball should be 'SUMO' or not, i.e. whether or not to include the
326     external dependency tarballs. Note that we always include
327     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
328     is included as well.
329     """
330
331     user_options = sdist.sdist.user_options + \
332         [('sumo', 's',
333           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
334          ]
335     boolean_options = ['sumo']
336
337     def initialize_options(self):
338         sdist.sdist.initialize_options(self)
339         self.sumo = False
340
341     def make_distribution(self):
342         # add our extra files to the list just before building the
343         # tarball/zipfile. We override make_distribution() instead of run()
344         # because setuptools.command.sdist.run() does not lend itself to
345         # easy/robust subclassing (the code we need to add goes right smack
346         # in the middle of a 12-line method). If this were the distutils
347         # version, we'd override get_file_list().
348
349         if self.sumo:
350             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
351             # We assume that the user has fetched the tahoe-deps.tar.gz
352             # tarball and unpacked it already.
353             self.filelist.extend([os.path.join("tahoe-deps", fn)
354                                   for fn in os.listdir("tahoe-deps")])
355             # In addition, we want the tarball/zipfile to have -SUMO in the
356             # name, and the unpacked directory to have -SUMO too. The easiest
357             # way to do this is to patch self.distribution and override the
358             # get_fullname() method. (an alternative is to modify
359             # self.distribution.metadata.version, but that also affects the
360             # contents of PKG-INFO).
361             fullname = self.distribution.get_fullname()
362             def get_fullname():
363                 return fullname + "-SUMO"
364             self.distribution.get_fullname = get_fullname
365
366         return sdist.sdist.make_distribution(self)
367
368 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
369 # the _auto_deps.install_requires list, which is used in the call to setup()
370 # at the end of this file
371 from _auto_deps import install_requires
372
373 setup(name='allmydata-tahoe',
374       version=verstr,
375       description='secure, decentralized, fault-tolerant filesystem',
376       long_description=LONG_DESCRIPTION,
377       author='the allmydata.org Tahoe project',
378       author_email='tahoe-dev@allmydata.org',
379       url='http://allmydata.org/',
380       license='GNU GPL',
381       cmdclass={"show_supportlib": ShowSupportLib,
382                 "show_pythonpath": ShowPythonPath,
383                 "run_with_pythonpath": RunWithPythonPath,
384                 "check_auto_deps": CheckAutoDeps,
385                 "build_tahoe": BuildTahoe,
386                 "trial": Trial,
387                 "sdist": MySdist,
388                 },
389       package_dir = {'':'src'},
390       packages=find_packages("src"),
391       classifiers=trove_classifiers,
392       test_suite="allmydata.test",
393       install_requires=install_requires,
394       include_package_data=True,
395       setup_requires=setup_requires,
396       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
397       zip_safe=False, # We prefer unzipped for easier access.
398       )