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 egg = os.path.realpath(glob.glob('setuptools-*.egg')[0])
42 sys.path.insert(0, egg)
43 egg = os.path.realpath(glob.glob('darcsver-*.egg')[0])
44 sys.path.insert(0, egg)
45 import setuptools; setuptools.bootstrap_install_from = egg
47 from setuptools import find_packages, setup
48 from setuptools.command import sdist
49 from setuptools import Command
52 "Development Status :: 5 - Production/Stable",
53 "Environment :: Console",
54 "Environment :: Web Environment",
55 "License :: OSI Approved :: GNU General Public License (GPL)",
56 "License :: DFSG approved",
57 "License :: Other/Proprietary License",
58 "Intended Audience :: Developers",
59 "Intended Audience :: End Users/Desktop",
60 "Intended Audience :: System Administrators",
61 "Operating System :: Microsoft",
62 "Operating System :: Microsoft :: Windows",
63 "Operating System :: Microsoft :: Windows :: Windows NT/2000",
64 "Operating System :: Unix",
65 "Operating System :: POSIX :: Linux",
66 "Operating System :: POSIX",
67 "Operating System :: MacOS :: MacOS X",
68 "Operating System :: OS Independent",
69 "Natural Language :: English",
70 "Programming Language :: C",
71 "Programming Language :: Python",
72 "Programming Language :: Python :: 2",
73 "Programming Language :: Python :: 2.4",
74 "Programming Language :: Python :: 2.5",
75 "Programming Language :: Python :: 2.6",
77 "Topic :: System :: Systems Administration",
78 "Topic :: System :: Filesystems",
79 "Topic :: System :: Distributed Computing",
80 "Topic :: Software Development :: Libraries",
81 "Topic :: Communications :: Usenet News",
82 "Topic :: System :: Archiving :: Backup",
83 "Topic :: System :: Archiving :: Mirroring",
84 "Topic :: System :: Archiving",
90 # The darcsver command from the darcsver plugin is needed to initialize the
91 # distribution's .version attribute correctly. (It does this either by
92 # examining darcs history, or if that fails by reading the
93 # src/allmydata/_version.py file). darcsver will also write a new version
94 # stamp in src/allmydata/_version.py, with a version number derived from
95 # darcs history. Note that the setup.cfg file has an "[aliases]" section
96 # which enumerates commands that you might run and specifies that it will run
97 # darcsver before each one. If you add different commands (or if I forgot
98 # some that are already in use), you may need to add it to setup.cfg and
99 # configure it to run darcsver before your command, if you want the version
100 # number to be correct when that command runs.
101 # http://pypi.python.org/pypi/darcsver
102 setup_requires.append('darcsver >= 1.2.0')
104 # Nevow requires Twisted to setup, but doesn't declare that requirement in a
105 # way that enables setuptools to satisfy that requirement before Nevow's
106 # setup.py tried to "import twisted". Fortunately we require setuptools_trial
107 # to setup and setuptools_trial requires Twisted to install, so hopefully
108 # everything will work out until the Nevow issue is fixed:
109 # http://divmod.org/trac/ticket/2629 setuptools_trial is needed if you want
110 # "./setup.py trial" or "./setup.py test" to execute the tests (and in order
111 # to make sure Twisted is installed early enough -- see the paragraph above).
112 # http://pypi.python.org/pypi/setuptools_trial
113 setup_requires.extend(['setuptools_trial >= 0.5'])
115 # setuptools_darcs is required to produce complete distributions (such as
116 # with "sdist" or "bdist_egg") (unless there is a PKG-INFO file present which
117 # shows that this is itself a source distribution). For simplicity, and
118 # because there is some unknown error with setuptools_darcs when building and
119 # testing tahoe all in one python command on some platforms, we always add it
120 # to setup_requires. http://pypi.python.org/pypi/setuptools_darcs
121 setup_requires.append('setuptools_darcs >= 1.1.0')
123 # trialcoverage is required if you want the "trial" unit test runner to have a
124 # "--reporter=bwverbose-coverage" option which produces code-coverage results.
125 # The required version is 0.3.3, because that is the latest version that only
126 # depends on a version of pycoverage for which binary packages are available.
127 if "--reporter=bwverbose-coverage" in sys.argv:
128 setup_requires.append('trialcoverage >= 0.3.3')
130 # stdeb is required to produce Debian files with the "sdist_dsc" command.
131 if "sdist_dsc" in sys.argv:
132 setup_requires.append('stdeb >= 0.3')
135 # Mock - Mocking and Testing Library
136 # http://www.voidspace.org.uk/python/mock/
140 class ShowSupportLib(Command):
142 def initialize_options(self):
144 def finalize_options(self):
147 # TODO: --quiet suppresses the 'running show_supportlib' message.
148 # Find a way to do this all the time.
149 print supportlib # TODO windowsy
151 class ShowPythonPath(Command):
153 def initialize_options(self):
155 def finalize_options(self):
158 # TODO: --quiet suppresses the 'running show_supportlib' message.
159 # Find a way to do this all the time.
160 print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
162 class RunWithPythonPath(Command):
163 description = "Run a subcommand with PYTHONPATH set appropriately"
165 user_options = [ ("python", "p",
166 "Treat command string as arguments to a python executable"),
167 ("command=", "c", "Command to be run"),
168 ("directory=", "d", "Directory to run the command in"),
170 boolean_options = ["python"]
172 def initialize_options(self):
175 self.directory = None
176 def finalize_options(self):
179 oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
181 # grr silly split() behavior
183 os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
185 # We must require the command to be safe to split on
186 # whitespace, and have --python and --directory to make it
187 # easier to achieve this.
191 command.append(sys.executable)
193 command.extend(self.command.split())
195 raise RuntimeError("The --command argument is mandatory")
197 os.chdir(self.directory)
199 print "command =", " ".join(command)
200 rc = subprocess.call(command)
203 class TestMacDiskImage(Command):
205 def initialize_options(self):
207 def finalize_options(self):
211 sys.path.append(os.path.join('misc', 'build_helpers'))
212 import test_mac_diskimage
213 return test_mac_diskimage.test_mac_diskimage('Allmydata', version=self.distribution.metadata.version)
215 class CheckAutoDeps(Command):
217 def initialize_options(self):
219 def finalize_options(self):
223 execfile('src/allmydata/_auto_deps.py', adglobals)
224 adglobals['require_auto_deps']()
227 class MakeExecutable(Command):
229 def initialize_options(self):
231 def finalize_options(self):
234 bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
236 if sys.platform == 'win32':
237 # 'tahoe' script is needed for cygwin
238 script_names = ["tahoe.pyscript", "tahoe"]
240 script_names = ["tahoe"]
242 # Create the tahoe script file under the 'bin' directory. This
243 # file is exactly the same as the 'tahoe-script.template' script
244 # except that the shebang line is rewritten to use our sys.executable
245 # for the interpreter.
246 f = open(bin_tahoe_template, "rU")
247 script_lines = f.readlines()
249 script_lines[0] = '#!%s\n' % (sys.executable,)
250 for script_name in script_names:
251 tahoe_script = os.path.join("bin", script_name)
253 os.remove(tahoe_script)
255 if os.path.exists(tahoe_script):
257 f = open(tahoe_script, "wb")
258 for line in script_lines:
263 old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE])
264 new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
265 stat.S_IXGRP | stat.S_IRGRP |
266 stat.S_IXOTH | stat.S_IROTH )
267 os.chmod(tahoe_script, new_mode)
269 old_tahoe_exe = os.path.join("bin", "tahoe.exe")
271 os.remove(old_tahoe_exe)
273 if os.path.exists(old_tahoe_exe):
277 class MySdist(sdist.sdist):
278 """ A hook in the sdist command so that we can determine whether this the
279 tarball should be 'SUMO' or not, i.e. whether or not to include the
280 external dependency tarballs. Note that we always include
281 misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
285 user_options = sdist.sdist.user_options + \
287 "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
289 boolean_options = ['sumo']
291 def initialize_options(self):
292 sdist.sdist.initialize_options(self)
295 def make_distribution(self):
296 # add our extra files to the list just before building the
297 # tarball/zipfile. We override make_distribution() instead of run()
298 # because setuptools.command.sdist.run() does not lend itself to
299 # easy/robust subclassing (the code we need to add goes right smack
300 # in the middle of a 12-line method). If this were the distutils
301 # version, we'd override get_file_list().
304 # If '--sumo' was specified, include tahoe-deps/* in the sdist.
305 # We assume that the user has fetched the tahoe-deps.tar.gz
306 # tarball and unpacked it already.
307 self.filelist.extend([os.path.join("tahoe-deps", fn)
308 for fn in os.listdir("tahoe-deps")])
309 # In addition, we want the tarball/zipfile to have -SUMO in the
310 # name, and the unpacked directory to have -SUMO too. The easiest
311 # way to do this is to patch self.distribution and override the
312 # get_fullname() method. (an alternative is to modify
313 # self.distribution.metadata.version, but that also affects the
314 # contents of PKG-INFO).
315 fullname = self.distribution.get_fullname()
317 return fullname + "-SUMO"
318 self.distribution.get_fullname = get_fullname
320 return sdist.sdist.make_distribution(self)
322 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
323 # the _auto_deps.install_requires list, which is used in the call to setup()
326 execfile('src/allmydata/_auto_deps.py', adglobals)
327 install_requires = adglobals['install_requires']
329 APPNAME='allmydata-tahoe'
330 APPNAMEFILE = os.path.join('src', 'allmydata', '_appname.py')
331 APPNAMEFILESTR = "__appname__ = '%s'" % (APPNAME,)
333 curappnamefilestr = open(APPNAMEFILE, 'rU').read()
334 except EnvironmentError:
335 # No file, or unreadable or something, okay then let's try to write one.
336 open(APPNAMEFILE, "w").write(APPNAMEFILESTR)
338 if curappnamefilestr.strip() != APPNAMEFILESTR:
339 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)
344 setup_args["version"] = version
347 description='secure, decentralized, fault-tolerant filesystem',
348 long_description=open('README.txt', 'rU').read(),
349 author='the Tahoe-LAFS project',
350 author_email='tahoe-dev@tahoe-lafs.org',
351 url='http://tahoe-lafs.org/',
352 license='GNU GPL', # see README.txt -- there is an alternative licence
353 cmdclass={"show_supportlib": ShowSupportLib,
354 "show_pythonpath": ShowPythonPath,
355 "run_with_pythonpath": RunWithPythonPath,
356 "check_auto_deps": CheckAutoDeps,
357 "test_mac_diskimage": TestMacDiskImage,
358 "make_executable": MakeExecutable,
361 package_dir = {'':'src'},
362 packages=find_packages("src"),
363 classifiers=trove_classifiers,
364 test_suite="allmydata.test",
365 install_requires=install_requires,
366 tests_require=tests_require,
367 include_package_data=True,
368 setup_requires=setup_requires,
369 entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
370 zip_safe=False, # We prefer unzipped for easier access.