2 # -*- coding: utf-8 -*-
3 import sys; assert sys.version_info < (3,), ur"Tahoe-LAFS does not run under Python 3. Please use Python 2.7.x."
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.7",
105 "Topic :: Utilities",
106 "Topic :: System :: Systems Administration",
107 "Topic :: System :: Filesystems",
108 "Topic :: System :: Distributed Computing",
109 "Topic :: Software Development :: Libraries",
110 "Topic :: System :: Archiving :: Backup",
111 "Topic :: System :: Archiving :: Mirroring",
112 "Topic :: System :: Archiving",
116 # We no longer have any requirements specific to tests.
120 class Trial(Command):
121 description = "run trial (use 'bin%stahoe debug trial' for the full set of trial options)" % (os.sep,)
122 # This is just a subset of the most useful options, for compatibility.
123 user_options = [ ("no-rterrors", None, "Don't print out tracebacks as they occur."),
124 ("rterrors", "e", "Print out tracebacks as they occur (default, so ignored)."),
125 ("until-failure", "u", "Repeat a test (specified by -s) until it fails."),
126 ("reporter=", None, "The reporter to use for this test run."),
127 ("suite=", "s", "Specify the test suite."),
128 ("quiet", None, "Don't display version numbers and paths of Tahoe dependencies."),
129 ("coverage", "c", "Collect branch coverage information."),
132 def initialize_options(self):
133 self.rterrors = False
134 self.no_rterrors = False
135 self.until_failure = False
137 self.suite = "allmydata"
139 self.coverage = False
141 def finalize_options(self):
145 args = [sys.executable, os.path.join('bin', 'tahoe')]
148 from errno import ENOENT
149 coverage_cmd = 'coverage'
151 subprocess.call([coverage_cmd, 'help'])
153 if e.errno != ENOENT:
155 coverage_cmd = 'python-coverage'
157 rc = subprocess.call([coverage_cmd, 'help'])
159 if e.errno != ENOENT:
162 print >>sys.stderr, "Couldn't find the command 'coverage' nor 'python-coverage'."
163 print >>sys.stderr, "coverage can be installed using 'pip install coverage', or on Debian-based systems, 'apt-get install python-coverage'."
166 args += ['@' + coverage_cmd, 'run', '--branch', '--source=src/allmydata', '@tahoe']
169 args.append('--version-and-path')
170 args += ['debug', 'trial']
171 if self.rterrors and self.no_rterrors:
172 raise AssertionError("--rterrors and --no-rterrors conflict.")
173 if not self.no_rterrors:
174 args.append('--rterrors')
175 if self.until_failure:
176 args.append('--until-failure')
178 args.append('--reporter=' + self.reporter)
180 args.append(self.suite)
181 rc = subprocess.call(args)
185 class MakeExecutable(Command):
186 description = "make the 'bin%stahoe' scripts" % (os.sep,)
189 def initialize_options(self):
191 def finalize_options(self):
194 bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
196 # tahoe.pyscript is really only necessary for Windows, but we also
197 # create it on Unix for consistency.
198 script_names = ["tahoe.pyscript", "tahoe"]
200 # Create the tahoe script file under the 'bin' directory. This
201 # file is exactly the same as the 'tahoe-script.template' script
202 # except that the shebang line is rewritten to use our sys.executable
203 # for the interpreter.
204 f = open(bin_tahoe_template, "rU")
205 script_lines = f.readlines()
207 script_lines[0] = '#!%s\n' % (sys.executable,)
208 for script_name in script_names:
209 tahoe_script = os.path.join("bin", script_name)
211 os.remove(tahoe_script)
213 if os.path.exists(tahoe_script):
215 f = open(tahoe_script, "wb")
216 for line in script_lines:
221 unix_script = os.path.join("bin", "tahoe")
222 old_mode = stat.S_IMODE(os.stat(unix_script)[stat.ST_MODE])
223 new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
224 stat.S_IXGRP | stat.S_IRGRP |
225 stat.S_IXOTH | stat.S_IROTH )
226 os.chmod(unix_script, new_mode)
228 old_tahoe_exe = os.path.join("bin", "tahoe.exe")
230 os.remove(old_tahoe_exe)
232 if os.path.exists(old_tahoe_exe):
236 GIT_VERSION_BODY = '''
237 # This _version.py is generated from git metadata by the tahoe setup.py.
239 __pkgname__ = %(pkgname)r
240 real_version = %(version)r
241 full_version = %(full)r
243 verstr = %(normalized)r
247 def run_command(args, cwd=None):
248 use_shell = sys.platform == "win32"
250 p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd, shell=use_shell)
251 except EnvironmentError as e: # if this gives a SyntaxError, note that Tahoe-LAFS requires Python 2.7+
252 print("Warning: unable to run %r." % (" ".join(args),))
255 stdout = p.communicate()[0].strip()
256 if p.returncode != 0:
257 print("Warning: %r returned error code %r." % (" ".join(args), p.returncode))
262 def versions_from_git(tag_prefix):
263 # This runs 'git' from the directory that contains this file. That either
264 # means someone ran a setup.py command (and this code is in
265 # versioneer.py, thus the containing directory is the root of the source
266 # tree), or someone ran a project-specific entry point (and this code is
267 # in _version.py, thus the containing directory is somewhere deeper in
268 # the source tree). This only gets called if the git-archive 'subst'
269 # variables were *not* expanded, and _version.py hasn't already been
270 # rewritten with a short version string, meaning we're inside a checked
273 # versions_from_git (as copied from python-versioneer) returns strings
274 # like "1.9.0-25-gb73aba9-dirty", which means we're in a tree with
275 # uncommited changes (-dirty), the latest checkin is revision b73aba9,
276 # the most recent tag was 1.9.0, and b73aba9 has 25 commits that weren't
277 # in 1.9.0 . The narrow-minded NormalizedVersion parser that takes our
278 # output (meant to enable sorting of version strings) refuses most of
279 # that. Tahoe uses a function named suggest_normalized_version() that can
280 # handle "1.9.0.post25", so dumb down our output to match.
283 source_dir = os.path.dirname(os.path.abspath(__file__))
284 except NameError as e:
285 # some py2exe/bbfreeze/non-CPython implementations don't do __file__
286 print("Warning: unable to find version because we could not obtain the source directory.")
289 stdout = run_command(["git", "describe", "--tags", "--dirty", "--always"],
292 # run_command already complained.
294 if not stdout.startswith(tag_prefix):
295 print("Warning: tag %r doesn't start with prefix %r." % (stdout, tag_prefix))
297 version = stdout[len(tag_prefix):]
298 pieces = version.split("-")
300 normalized_version = pieces[0]
302 normalized_version = "%s.post%s" % (pieces[0], pieces[1])
304 stdout = run_command(["git", "rev-parse", "HEAD"], cwd=source_dir)
306 # run_command already complained.
308 full = stdout.strip()
309 if version.endswith("-dirty"):
311 normalized_version += ".dev0"
313 # Thanks to Jistanidiot at <http://stackoverflow.com/questions/6245570/get-current-branch-name>.
314 stdout = run_command(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=source_dir)
315 branch = (stdout or "unknown").strip()
317 return {"version": version, "normalized": normalized_version, "full": full, "branch": branch}
319 # setup.cfg has an [aliases] section which runs "update_version" before many
320 # commands (like "build" and "sdist") that need to know our package version
321 # ahead of time. If you add different commands (or if we forgot some), you
322 # may need to add it to setup.cfg and configure it to run update_version
323 # before your command.
325 class UpdateVersion(Command):
326 description = "update _version.py from revision-control metadata"
329 def initialize_options(self):
331 def finalize_options(self):
336 if os.path.isdir(os.path.join(basedir, ".git")):
337 verstr = self.try_from_git()
340 self.distribution.metadata.version = verstr
343 ********************************************************************
344 Warning: no version information found. This may cause tests to fail.
345 ********************************************************************
348 def try_from_git(self):
349 # If we change APPNAME, the release tag names should also change from then on.
350 versions = versions_from_git(APPNAME + '-')
352 f = open(VERSION_PY_FILENAME, "wb")
353 f.write(GIT_VERSION_BODY %
354 { "pkgname": self.distribution.get_name(),
355 "version": versions["version"],
356 "normalized": versions["normalized"],
357 "full": versions["full"],
358 "branch": versions["branch"],
361 print("Wrote normalized version %r into '%s'" % (versions["normalized"], VERSION_PY_FILENAME))
363 return versions.get("normalized", None)
366 class MySdist(sdist.sdist):
367 """ A hook in the sdist command so that we can determine whether this the
368 tarball should be 'SUMO' or not, i.e. whether or not to include the
369 external dependency tarballs. Note that we always include
370 misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
374 user_options = sdist.sdist.user_options + \
376 "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
378 boolean_options = ['sumo']
380 def initialize_options(self):
381 sdist.sdist.initialize_options(self)
384 def make_distribution(self):
385 # add our extra files to the list just before building the
386 # tarball/zipfile. We override make_distribution() instead of run()
387 # because setuptools.command.sdist.run() does not lend itself to
388 # easy/robust subclassing (the code we need to add goes right smack
389 # in the middle of a 12-line method). If this were the distutils
390 # version, we'd override get_file_list().
393 # If '--sumo' was specified, include tahoe-deps/* in the sdist.
394 # We assume that the user has fetched the tahoe-deps.tar.gz
395 # tarball and unpacked it already.
396 self.filelist.extend([os.path.join("tahoe-deps", fn)
397 for fn in os.listdir("tahoe-deps")])
398 # In addition, we want the tarball/zipfile to have -SUMO in the
399 # name, and the unpacked directory to have -SUMO too. The easiest
400 # way to do this is to patch self.distribution and override the
401 # get_fullname() method. (an alternative is to modify
402 # self.distribution.metadata.version, but that also affects the
403 # contents of PKG-INFO).
404 fullname = self.distribution.get_fullname()
406 return fullname + "-SUMO"
407 self.distribution.get_fullname = get_fullname
410 old_mask = os.umask(int("022", 8))
411 return sdist.sdist.make_distribution(self)
418 setup_args["version"] = version
421 description='secure, decentralized, fault-tolerant file store',
422 long_description=open('README.rst', 'rU').read(),
423 author='the Tahoe-LAFS project',
424 author_email='tahoe-dev@tahoe-lafs.org',
425 url='https://tahoe-lafs.org/',
426 license='GNU GPL', # see README.rst -- there is an alternative licence
427 cmdclass={"trial": Trial,
428 "make_executable": MakeExecutable,
429 "update_version": UpdateVersion,
432 package_dir = {'':'src'},
433 packages=['allmydata',
434 'allmydata.frontends',
435 'allmydata.immutable',
436 'allmydata.immutable.downloader',
437 'allmydata.introducer',
446 classifiers=trove_classifiers,
447 test_suite="allmydata.test",
448 install_requires=install_requires,
449 tests_require=tests_require,
450 package_data={"allmydata.web": ["*.xhtml",
451 "static/*.js", "static/*.png", "static/*.css",
456 setup_requires=setup_requires,
457 entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
458 zip_safe=False, # We prefer unzipped for easier access.