2 # -*- coding: utf-8 -*-
3 import sys; assert sys.version_info < (3,), ur"Tahoe-LAFS does not run under Python 3. Please use a version of Python between 2.6 and 2.7.x inclusive."
5 # Tahoe-LAFS -- secure, distributed storage grid
7 # Copyright © 2006-2012 The Tahoe-LAFS Software Foundation
9 # This file is part of Tahoe-LAFS.
11 # See the docs/about.rst file for licensing information.
13 import os, stat, subprocess, re
15 ##### sys.path management
17 def pylibdir(prefixdir):
18 pyver = "python%d.%d" % (sys.version_info[:2])
19 if sys.platform == "win32":
20 return os.path.join(prefixdir, "Lib", "site-packages")
22 return os.path.join(prefixdir, "lib", pyver, "site-packages")
24 basedir = os.path.dirname(os.path.abspath(__file__))
25 supportlib = pylibdir(os.path.join(basedir, "support"))
27 # locate our version number
29 def read_version_py(infname):
31 verstrline = open(infname, "rt").read()
32 except EnvironmentError:
35 VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
36 mo = re.search(VSRE, verstrline, re.M)
40 VERSION_PY_FILENAME = 'src/allmydata/_version.py'
41 version = read_version_py(VERSION_PY_FILENAME)
43 APPNAME='allmydata-tahoe'
44 APPNAMEFILE = os.path.join('src', 'allmydata', '_appname.py')
45 APPNAMEFILESTR = "__appname__ = '%s'" % (APPNAME,)
47 curappnamefilestr = open(APPNAMEFILE, 'rU').read()
48 except EnvironmentError:
49 # No file, or unreadable or something, okay then let's try to write one.
50 open(APPNAMEFILE, "w").write(APPNAMEFILESTR)
52 if curappnamefilestr.strip() != APPNAMEFILESTR:
53 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))
56 # setuptools/zetuptoolz looks in __main__.__requires__ for a list of
57 # requirements. When running "python setup.py test", __main__ is
58 # setup.py, so we put the list here so that the requirements will be
59 # available for tests:
61 # Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
62 # the _auto_deps.install_requires list, which is used in the call to setup()
65 execfile('src/allmydata/_auto_deps.py', adglobals)
66 install_requires = adglobals['install_requires']
67 setup_requires = adglobals['setup_requires']
69 if len(sys.argv) > 1 and sys.argv[1] == '--fakedependency':
71 install_requires += ["fakedependency >= 1.0.0"]
73 __requires__ = install_requires[:]
75 egg = os.path.realpath('setuptools-0.6c16dev6.egg')
76 sys.path.insert(0, egg)
77 import setuptools; setuptools.bootstrap_install_from = egg
79 from setuptools import 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 :: Unix",
96 "Operating System :: POSIX :: Linux",
97 "Operating System :: POSIX",
98 "Operating System :: MacOS :: MacOS X",
99 "Operating System :: OS Independent",
100 "Natural Language :: English",
101 "Programming Language :: C",
102 "Programming Language :: Python",
103 "Programming Language :: Python :: 2",
104 "Programming Language :: Python :: 2.6",
105 "Programming Language :: Python :: 2.7",
106 "Topic :: Utilities",
107 "Topic :: System :: Systems Administration",
108 "Topic :: System :: Filesystems",
109 "Topic :: System :: Distributed Computing",
110 "Topic :: Software Development :: Libraries",
111 "Topic :: System :: Archiving :: Backup",
112 "Topic :: System :: Archiving :: Mirroring",
113 "Topic :: System :: Archiving",
117 # We no longer have any requirements specific to tests.
121 class Trial(Command):
122 description = "run trial (use 'bin%stahoe debug trial' for the full set of trial options)" % (os.sep,)
123 # This is just a subset of the most useful options, for compatibility.
124 user_options = [ ("no-rterrors", None, "Don't print out tracebacks as they occur."),
125 ("rterrors", "e", "Print out tracebacks as they occur (default, so ignored)."),
126 ("until-failure", "u", "Repeat a test (specified by -s) until it fails."),
127 ("reporter=", None, "The reporter to use for this test run."),
128 ("suite=", "s", "Specify the test suite."),
129 ("quiet", None, "Don't display version numbers and paths of Tahoe dependencies."),
130 ("coverage", "c", "Collect branch coverage information."),
133 def initialize_options(self):
134 self.rterrors = False
135 self.no_rterrors = False
136 self.until_failure = False
138 self.suite = "allmydata"
140 self.coverage = False
142 def finalize_options(self):
146 args = [sys.executable, os.path.join('bin', 'tahoe')]
149 from errno import ENOENT
150 coverage_cmd = 'coverage'
152 subprocess.call([coverage_cmd, 'help'])
154 if e.errno != ENOENT:
156 coverage_cmd = 'python-coverage'
158 rc = subprocess.call([coverage_cmd, 'help'])
160 if e.errno != ENOENT:
163 print >>sys.stderr, "Couldn't find the command 'coverage' nor 'python-coverage'."
164 print >>sys.stderr, "coverage can be installed using 'pip install coverage', or on Debian-based systems, 'apt-get install python-coverage'."
167 args += ['@' + coverage_cmd, 'run', '--branch', '--source=src/allmydata', '@tahoe']
170 args.append('--version-and-path')
171 args += ['debug', 'trial']
172 if self.rterrors and self.no_rterrors:
173 raise AssertionError("--rterrors and --no-rterrors conflict.")
174 if not self.no_rterrors:
175 args.append('--rterrors')
176 if self.until_failure:
177 args.append('--until-failure')
179 args.append('--reporter=' + self.reporter)
181 args.append(self.suite)
182 rc = subprocess.call(args)
186 class MakeExecutable(Command):
187 description = "make the 'bin%stahoe' scripts" % (os.sep,)
190 def initialize_options(self):
192 def finalize_options(self):
195 bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
197 # tahoe.pyscript is really only necessary for Windows, but we also
198 # create it on Unix for consistency.
199 script_names = ["tahoe.pyscript", "tahoe"]
201 # Create the tahoe script file under the 'bin' directory. This
202 # file is exactly the same as the 'tahoe-script.template' script
203 # except that the shebang line is rewritten to use our sys.executable
204 # for the interpreter.
205 f = open(bin_tahoe_template, "rU")
206 script_lines = f.readlines()
208 script_lines[0] = '#!%s\n' % (sys.executable,)
209 for script_name in script_names:
210 tahoe_script = os.path.join("bin", script_name)
212 os.remove(tahoe_script)
214 if os.path.exists(tahoe_script):
216 f = open(tahoe_script, "wb")
217 for line in script_lines:
222 unix_script = os.path.join("bin", "tahoe")
223 old_mode = stat.S_IMODE(os.stat(unix_script)[stat.ST_MODE])
224 new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
225 stat.S_IXGRP | stat.S_IRGRP |
226 stat.S_IXOTH | stat.S_IROTH )
227 os.chmod(unix_script, new_mode)
229 old_tahoe_exe = os.path.join("bin", "tahoe.exe")
231 os.remove(old_tahoe_exe)
233 if os.path.exists(old_tahoe_exe):
237 GIT_VERSION_BODY = '''
238 # This _version.py is generated from git metadata by the tahoe setup.py.
240 __pkgname__ = %(pkgname)r
241 real_version = %(version)r
242 full_version = %(full)r
244 verstr = %(normalized)r
248 def run_command(args, cwd=None):
249 use_shell = sys.platform == "win32"
251 p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd, shell=use_shell)
252 except EnvironmentError as e: # if this gives a SyntaxError, note that Tahoe-LAFS requires Python 2.6+
253 print("Warning: unable to run %r." % (" ".join(args),))
256 stdout = p.communicate()[0].strip()
257 if p.returncode != 0:
258 print("Warning: %r returned error code %r." % (" ".join(args), p.returncode))
263 def versions_from_git(tag_prefix):
264 # This runs 'git' from the directory that contains this file. That either
265 # means someone ran a setup.py command (and this code is in
266 # versioneer.py, thus the containing directory is the root of the source
267 # tree), or someone ran a project-specific entry point (and this code is
268 # in _version.py, thus the containing directory is somewhere deeper in
269 # the source tree). This only gets called if the git-archive 'subst'
270 # variables were *not* expanded, and _version.py hasn't already been
271 # rewritten with a short version string, meaning we're inside a checked
274 # versions_from_git (as copied from python-versioneer) returns strings
275 # like "1.9.0-25-gb73aba9-dirty", which means we're in a tree with
276 # uncommited changes (-dirty), the latest checkin is revision b73aba9,
277 # the most recent tag was 1.9.0, and b73aba9 has 25 commits that weren't
278 # in 1.9.0 . The narrow-minded NormalizedVersion parser that takes our
279 # output (meant to enable sorting of version strings) refuses most of
280 # that. Tahoe uses a function named suggest_normalized_version() that can
281 # handle "1.9.0.post25", so dumb down our output to match.
284 source_dir = os.path.dirname(os.path.abspath(__file__))
285 except NameError as e:
286 # some py2exe/bbfreeze/non-CPython implementations don't do __file__
287 print("Warning: unable to find version because we could not obtain the source directory.")
290 stdout = run_command(["git", "describe", "--tags", "--dirty", "--always"],
293 # run_command already complained.
295 if not stdout.startswith(tag_prefix):
296 print("Warning: tag %r doesn't start with prefix %r." % (stdout, tag_prefix))
298 version = stdout[len(tag_prefix):]
299 pieces = version.split("-")
301 normalized_version = pieces[0]
303 normalized_version = "%s.post%s" % (pieces[0], pieces[1])
305 stdout = run_command(["git", "rev-parse", "HEAD"], cwd=source_dir)
307 # run_command already complained.
309 full = stdout.strip()
310 if version.endswith("-dirty"):
312 normalized_version += ".dev0"
314 # Thanks to Jistanidiot at <http://stackoverflow.com/questions/6245570/get-current-branch-name>.
315 stdout = run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=source_dir)
316 branch = (stdout or "unknown").strip()
318 return {"version": version, "normalized": normalized_version, "full": full, "branch": branch}
320 # setup.cfg has an [aliases] section which runs "update_version" before many
321 # commands (like "build" and "sdist") that need to know our package version
322 # ahead of time. If you add different commands (or if we forgot some), you
323 # may need to add it to setup.cfg and configure it to run update_version
324 # before your command.
326 class UpdateVersion(Command):
327 description = "update _version.py from revision-control metadata"
330 def initialize_options(self):
332 def finalize_options(self):
337 if os.path.isdir(os.path.join(basedir, ".git")):
338 verstr = self.try_from_git()
341 self.distribution.metadata.version = verstr
344 ********************************************************************
345 Warning: no version information found. This may cause tests to fail.
346 ********************************************************************
349 def try_from_git(self):
350 # If we change APPNAME, the release tag names should also change from then on.
351 versions = versions_from_git(APPNAME + '-')
353 f = open(VERSION_PY_FILENAME, "wb")
354 f.write(GIT_VERSION_BODY %
355 { "pkgname": self.distribution.get_name(),
356 "version": versions["version"],
357 "normalized": versions["normalized"],
358 "full": versions["full"],
359 "branch": versions["branch"],
362 print("Wrote normalized version %r into '%s'" % (versions["normalized"], VERSION_PY_FILENAME))
364 return versions.get("normalized", None)
367 class MySdist(sdist.sdist):
368 """ A hook in the sdist command so that we can determine whether this the
369 tarball should be 'SUMO' or not, i.e. whether or not to include the
370 external dependency tarballs. Note that we always include
371 misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
375 user_options = sdist.sdist.user_options + \
377 "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
379 boolean_options = ['sumo']
381 def initialize_options(self):
382 sdist.sdist.initialize_options(self)
385 def make_distribution(self):
386 # add our extra files to the list just before building the
387 # tarball/zipfile. We override make_distribution() instead of run()
388 # because setuptools.command.sdist.run() does not lend itself to
389 # easy/robust subclassing (the code we need to add goes right smack
390 # in the middle of a 12-line method). If this were the distutils
391 # version, we'd override get_file_list().
394 # If '--sumo' was specified, include tahoe-deps/* in the sdist.
395 # We assume that the user has fetched the tahoe-deps.tar.gz
396 # tarball and unpacked it already.
397 self.filelist.extend([os.path.join("tahoe-deps", fn)
398 for fn in os.listdir("tahoe-deps")])
399 # In addition, we want the tarball/zipfile to have -SUMO in the
400 # name, and the unpacked directory to have -SUMO too. The easiest
401 # way to do this is to patch self.distribution and override the
402 # get_fullname() method. (an alternative is to modify
403 # self.distribution.metadata.version, but that also affects the
404 # contents of PKG-INFO).
405 fullname = self.distribution.get_fullname()
407 return fullname + "-SUMO"
408 self.distribution.get_fullname = get_fullname
411 old_mask = os.umask(int("022", 8))
412 return sdist.sdist.make_distribution(self)
419 setup_args["version"] = version
422 description='secure, decentralized, fault-tolerant file store',
423 long_description=open('README.rst', 'rU').read(),
424 author='the Tahoe-LAFS project',
425 author_email='tahoe-dev@tahoe-lafs.org',
426 url='https://tahoe-lafs.org/',
427 license='GNU GPL', # see README.rst -- there is an alternative licence
428 cmdclass={"trial": Trial,
429 "make_executable": MakeExecutable,
430 "update_version": UpdateVersion,
433 package_dir = {'':'src'},
434 packages=['allmydata',
435 'allmydata.frontends',
436 'allmydata.immutable',
437 'allmydata.immutable.downloader',
438 'allmydata.introducer',
447 classifiers=trove_classifiers,
448 test_suite="allmydata.test",
449 install_requires=install_requires,
450 tests_require=tests_require,
451 package_data={"allmydata.web": ["*.xhtml",
452 "static/*.js", "static/*.png", "static/*.css",
457 setup_requires=setup_requires,
458 entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
459 zip_safe=False, # We prefer unzipped for easier access.