2 # -*- coding: utf-8 -*-
4 # Tahoe-LAFS -- secure, distributed storage grid
6 # Copyright © 2008-2010 Allmydata, Inc.
8 # This file is part of Tahoe-LAFS.
10 # See the docs/about.html file for licensing information.
12 import glob, os, shutil, stat, subprocess, sys, zipfile, re
14 ##### sys.path management
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")
21 return os.path.join(prefixdir, "lib", pyver, "site-packages")
23 basedir = os.path.dirname(os.path.abspath(__file__))
24 supportlib = pylibdir(os.path.join(basedir, "support"))
26 # locate our version number
28 def read_version_py(infname):
30 verstrline = open(infname, "rt").read()
31 except EnvironmentError:
34 VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
35 mo = re.search(VSRE, verstrline, re.M)
39 version = read_version_py("src/allmydata/_version.py")
41 APPNAME='allmydata-tahoe'
42 APPNAMEFILE = os.path.join('src', 'allmydata', '_appname.py')
43 APPNAMEFILESTR = "__appname__ = '%s'" % (APPNAME,)
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)
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)
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:
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()
63 execfile('src/allmydata/_auto_deps.py', adglobals)
64 install_requires = adglobals['install_requires']
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)
71 __requires__.append(APPNAME)
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
79 from setuptools import find_packages, setup
80 from setuptools.command import sdist
81 from setuptools import Command
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 "Topic :: Utilities",
109 "Topic :: System :: Systems Administration",
110 "Topic :: System :: Filesystems",
111 "Topic :: System :: Distributed Computing",
112 "Topic :: Software Development :: Libraries",
113 "Topic :: Communications :: Usenet News",
114 "Topic :: System :: Archiving :: Backup",
115 "Topic :: System :: Archiving :: Mirroring",
116 "Topic :: System :: Archiving",
122 # The darcsver command from the darcsver plugin is needed to initialize the
123 # distribution's .version attribute correctly. (It does this either by
124 # examining darcs history, or if that fails by reading the
125 # src/allmydata/_version.py file). darcsver will also write a new version
126 # stamp in src/allmydata/_version.py, with a version number derived from
127 # darcs history. Note that the setup.cfg file has an "[aliases]" section
128 # which enumerates commands that you might run and specifies that it will run
129 # darcsver before each one. If you add different commands (or if I forgot
130 # some that are already in use), you may need to add it to setup.cfg and
131 # configure it to run darcsver before your command, if you want the version
132 # number to be correct when that command runs.
133 # http://pypi.python.org/pypi/darcsver
134 setup_requires.append('darcsver >= 1.2.0')
136 # Nevow requires Twisted to setup, but doesn't declare that requirement in a
137 # way that enables setuptools to satisfy that requirement before Nevow's
138 # setup.py tried to "import twisted". Fortunately we require setuptools_trial
139 # to setup and setuptools_trial requires Twisted to install, so hopefully
140 # everything will work out until the Nevow issue is fixed:
141 # http://divmod.org/trac/ticket/2629 setuptools_trial is needed if you want
142 # "./setup.py trial" or "./setup.py test" to execute the tests (and in order
143 # to make sure Twisted is installed early enough -- see the paragraph above).
144 # http://pypi.python.org/pypi/setuptools_trial
145 setup_requires.extend(['setuptools_trial >= 0.5'])
147 # setuptools_darcs is required to produce complete distributions (such as
148 # with "sdist" or "bdist_egg") (unless there is a PKG-INFO file present which
149 # shows that this is itself a source distribution). For simplicity, and
150 # because there is some unknown error with setuptools_darcs when building and
151 # testing tahoe all in one python command on some platforms, we always add it
152 # to setup_requires. http://pypi.python.org/pypi/setuptools_darcs
153 setup_requires.append('setuptools_darcs >= 1.1.0')
155 # trialcoverage is required if you want the "trial" unit test runner to have a
156 # "--reporter=bwverbose-coverage" option which produces code-coverage results.
157 # The required version is 0.3.3, because that is the latest version that only
158 # depends on a version of pycoverage for which binary packages are available.
159 if "--reporter=bwverbose-coverage" in sys.argv:
160 setup_requires.append('trialcoverage >= 0.3.3')
162 # stdeb is required to produce Debian files with the "sdist_dsc" command.
163 if "sdist_dsc" in sys.argv:
164 setup_requires.append('stdeb >= 0.3')
167 # Mock - Mocking and Testing Library
168 # http://www.voidspace.org.uk/python/mock/
172 class ShowSupportLib(Command):
174 def initialize_options(self):
176 def finalize_options(self):
179 # TODO: --quiet suppresses the 'running show_supportlib' message.
180 # Find a way to do this all the time.
181 print supportlib # TODO windowsy
183 class ShowPythonPath(Command):
185 def initialize_options(self):
187 def finalize_options(self):
190 # TODO: --quiet suppresses the 'running show_supportlib' message.
191 # Find a way to do this all the time.
192 print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
194 class RunWithPythonPath(Command):
195 description = "Run a subcommand with PYTHONPATH set appropriately"
197 user_options = [ ("python", "p",
198 "Treat command string as arguments to a python executable"),
199 ("command=", "c", "Command to be run"),
200 ("directory=", "d", "Directory to run the command in"),
202 boolean_options = ["python"]
204 def initialize_options(self):
207 self.directory = None
208 def finalize_options(self):
211 oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
213 # grr silly split() behavior
215 os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
217 # We must require the command to be safe to split on
218 # whitespace, and have --python and --directory to make it
219 # easier to achieve this.
223 command.append(sys.executable)
225 command.extend(self.command.split())
227 raise RuntimeError("The --command argument is mandatory")
229 os.chdir(self.directory)
231 print "command =", " ".join(command)
232 rc = subprocess.call(command)
235 class TestMacDiskImage(Command):
237 def initialize_options(self):
239 def finalize_options(self):
243 sys.path.append(os.path.join('misc', 'build_helpers'))
244 import test_mac_diskimage
245 return test_mac_diskimage.test_mac_diskimage('Allmydata', version=self.distribution.metadata.version)
247 class CheckAutoDeps(Command):
249 def initialize_options(self):
251 def finalize_options(self):
255 execfile('src/allmydata/_auto_deps.py', adglobals)
256 adglobals['require_auto_deps']()
259 class MakeExecutable(Command):
261 def initialize_options(self):
263 def finalize_options(self):
266 bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
268 if sys.platform == 'win32':
269 # 'tahoe' script is needed for cygwin
270 script_names = ["tahoe.pyscript", "tahoe"]
272 script_names = ["tahoe"]
274 # Create the tahoe script file under the 'bin' directory. This
275 # file is exactly the same as the 'tahoe-script.template' script
276 # except that the shebang line is rewritten to use our sys.executable
277 # for the interpreter.
278 f = open(bin_tahoe_template, "rU")
279 script_lines = f.readlines()
281 script_lines[0] = '#!%s\n' % (sys.executable,)
282 for script_name in script_names:
283 tahoe_script = os.path.join("bin", script_name)
285 os.remove(tahoe_script)
287 if os.path.exists(tahoe_script):
289 f = open(tahoe_script, "wb")
290 for line in script_lines:
295 old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE])
296 new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
297 stat.S_IXGRP | stat.S_IRGRP |
298 stat.S_IXOTH | stat.S_IROTH )
299 os.chmod(tahoe_script, new_mode)
301 old_tahoe_exe = os.path.join("bin", "tahoe.exe")
303 os.remove(old_tahoe_exe)
305 if os.path.exists(old_tahoe_exe):
309 class MySdist(sdist.sdist):
310 """ A hook in the sdist command so that we can determine whether this the
311 tarball should be 'SUMO' or not, i.e. whether or not to include the
312 external dependency tarballs. Note that we always include
313 misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
317 user_options = sdist.sdist.user_options + \
319 "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
321 boolean_options = ['sumo']
323 def initialize_options(self):
324 sdist.sdist.initialize_options(self)
327 def make_distribution(self):
328 # add our extra files to the list just before building the
329 # tarball/zipfile. We override make_distribution() instead of run()
330 # because setuptools.command.sdist.run() does not lend itself to
331 # easy/robust subclassing (the code we need to add goes right smack
332 # in the middle of a 12-line method). If this were the distutils
333 # version, we'd override get_file_list().
336 # If '--sumo' was specified, include tahoe-deps/* in the sdist.
337 # We assume that the user has fetched the tahoe-deps.tar.gz
338 # tarball and unpacked it already.
339 self.filelist.extend([os.path.join("tahoe-deps", fn)
340 for fn in os.listdir("tahoe-deps")])
341 # In addition, we want the tarball/zipfile to have -SUMO in the
342 # name, and the unpacked directory to have -SUMO too. The easiest
343 # way to do this is to patch self.distribution and override the
344 # get_fullname() method. (an alternative is to modify
345 # self.distribution.metadata.version, but that also affects the
346 # contents of PKG-INFO).
347 fullname = self.distribution.get_fullname()
349 return fullname + "-SUMO"
350 self.distribution.get_fullname = get_fullname
352 return sdist.sdist.make_distribution(self)
356 setup_args["version"] = version
359 description='secure, decentralized, fault-tolerant filesystem',
360 long_description=open('README.txt', 'rU').read(),
361 author='the Tahoe-LAFS project',
362 author_email='tahoe-dev@tahoe-lafs.org',
363 url='http://tahoe-lafs.org/',
364 license='GNU GPL', # see README.txt -- there is an alternative licence
365 cmdclass={"show_supportlib": ShowSupportLib,
366 "show_pythonpath": ShowPythonPath,
367 "run_with_pythonpath": RunWithPythonPath,
368 "check_auto_deps": CheckAutoDeps,
369 "test_mac_diskimage": TestMacDiskImage,
370 "make_executable": MakeExecutable,
373 package_dir = {'':'src'},
374 packages=find_packages("src"),
375 classifiers=trove_classifiers,
376 test_suite="allmydata.test",
377 install_requires=install_requires,
378 tests_require=tests_require,
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 versionfiles=['src/allmydata/_version.py',],