2 # -*- coding: utf-8 -*-
4 # Tahoe-LAFS -- secure, distributed storage grid
6 # Copyright © 2008-2011 Allmydata, Inc.
8 # This file is part of Tahoe-LAFS.
10 # See the docs/about.rst file for licensing information.
12 import glob, os, stat, subprocess, sys, 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 if len(sys.argv) > 1 and sys.argv[1] == '--fakedependency':
68 install_requires += ["fakedependency >= 1.0.0"]
70 __requires__ = install_requires[:]
72 egg = os.path.realpath(glob.glob('setuptools-*.egg')[0])
73 sys.path.insert(0, egg)
74 egg = os.path.realpath(glob.glob('darcsver-*.egg')[0])
75 sys.path.insert(0, egg)
76 import setuptools; setuptools.bootstrap_install_from = egg
78 from setuptools import setup
79 from setuptools.command import sdist
80 from setuptools import Command
83 "Development Status :: 5 - Production/Stable",
84 "Environment :: Console",
85 "Environment :: Web Environment",
86 "License :: OSI Approved :: GNU General Public License (GPL)",
87 "License :: DFSG approved",
88 "License :: Other/Proprietary License",
89 "Intended Audience :: Developers",
90 "Intended Audience :: End Users/Desktop",
91 "Intended Audience :: System Administrators",
92 "Operating System :: Microsoft",
93 "Operating System :: Microsoft :: Windows",
94 "Operating System :: Microsoft :: Windows :: Windows NT/2000",
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.4",
105 "Programming Language :: Python :: 2.5",
106 "Programming Language :: Python :: 2.6",
107 "Programming Language :: Python :: 2.7",
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.7.2')
136 # Nevow imports itself when building, which causes Twisted and zope.interface
137 # to be imported. We need to make sure that the versions of Twisted and
138 # zope.interface used at build time satisfy Nevow's requirements. If not
139 # then there are two problems:
140 # - prior to Nevow v0.9.33, Nevow didn't declare its dependency on Twisted
141 # in a way that enabled setuptools to satisfy that requirement at
143 # - some versions of zope.interface, e.g. v3.6.4, are incompatible with
144 # Nevow, and we need to avoid those both at build and run-time.
146 # This only matters when compatible versions of Twisted and zope.interface
147 # are not already installed. Retire this hack when
148 # https://bugs.launchpad.net/nevow/+bug/812537 has been fixed.
149 setup_requires += [req for req in install_requires if req.startswith('Twisted') or req.startswith('zope.interface')]
151 # trialcoverage is required if you want the "trial" unit test runner to have a
152 # "--reporter=bwverbose-coverage" option which produces code-coverage results.
153 # The required version is 0.3.3, because that is the latest version that only
154 # depends on a version of pycoverage for which binary packages are available.
155 if "--reporter=bwverbose-coverage" in sys.argv:
156 setup_requires.append('trialcoverage >= 0.3.3')
158 # stdeb is required to produce Debian files with the "sdist_dsc" command.
159 if "sdist_dsc" in sys.argv:
160 setup_requires.append('stdeb >= 0.3')
162 # We no longer have any requirements specific to tests.
166 class Trial(Command):
167 description = "run trial (use 'bin%stahoe debug trial' for the full set of trial options)" % (os.sep,)
168 # This is just a subset of the most useful options, for compatibility.
169 user_options = [ ("rterrors", "e", "Print out tracebacks as soon as they occur."),
170 ("reporter=", None, "The reporter to use for this test run."),
171 ("suite=", "s", "Specify the test suite."),
172 ("quiet", None, "Don't display version numbers and paths of Tahoe dependencies."),
175 def initialize_options(self):
176 self.rterrors = False
178 self.suite = "allmydata"
181 def finalize_options(self):
185 args = [sys.executable, os.path.join('bin', 'tahoe')]
187 args.append('--version-and-path')
188 args += ['debug', 'trial']
190 args.append('--rterrors')
192 args.append('--reporter=' + self.reporter)
194 args.append(self.suite)
195 rc = subprocess.call(args)
199 class MakeExecutable(Command):
200 description = "make the 'bin%stahoe' scripts" % (os.sep,)
203 def initialize_options(self):
205 def finalize_options(self):
208 bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
210 # tahoe.pyscript is really only necessary for Windows, but we also
211 # create it on Unix for consistency.
212 script_names = ["tahoe.pyscript", "tahoe"]
214 # Create the tahoe script file under the 'bin' directory. This
215 # file is exactly the same as the 'tahoe-script.template' script
216 # except that the shebang line is rewritten to use our sys.executable
217 # for the interpreter.
218 f = open(bin_tahoe_template, "rU")
219 script_lines = f.readlines()
221 script_lines[0] = '#!%s\n' % (sys.executable,)
222 for script_name in script_names:
223 tahoe_script = os.path.join("bin", script_name)
225 os.remove(tahoe_script)
227 if os.path.exists(tahoe_script):
229 f = open(tahoe_script, "wb")
230 for line in script_lines:
235 unix_script = os.path.join("bin", "tahoe")
236 old_mode = stat.S_IMODE(os.stat(unix_script)[stat.ST_MODE])
237 new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
238 stat.S_IXGRP | stat.S_IRGRP |
239 stat.S_IXOTH | stat.S_IROTH )
240 os.chmod(unix_script, new_mode)
242 old_tahoe_exe = os.path.join("bin", "tahoe.exe")
244 os.remove(old_tahoe_exe)
246 if os.path.exists(old_tahoe_exe):
250 DARCS_VERSION_BODY = '''
251 # This _version.py is generated from darcs metadata by the tahoe setup.py
252 # and the "darcsver" package.
254 __pkgname__ = "%(pkgname)s"
255 verstr = "%(pkgversion)s"
259 GIT_VERSION_BODY = '''
260 # This _version.py is generated from git metadata by the tahoe setup.py.
262 __pkgname__ = "%(pkgname)s"
263 real_version = "%(version)s"
264 full_version = "%(full)s"
265 verstr = "%(normalized)s"
269 def run_command(args, cwd=None, verbose=False):
271 # remember shell=False, so use git.cmd on windows, not just git
272 p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd)
273 except EnvironmentError, e:
275 print "unable to run %s" % args[0]
278 stdout = p.communicate()[0].strip()
279 if p.returncode != 0:
281 print "unable to run %s (error)" % args[0]
286 def versions_from_git(tag_prefix, verbose=False):
287 # this runs 'git' from the directory that contains this file. That either
288 # means someone ran a setup.py command (and this code is in
289 # versioneer.py, thus the containing directory is the root of the source
290 # tree), or someone ran a project-specific entry point (and this code is
291 # in _version.py, thus the containing directory is somewhere deeper in
292 # the source tree). This only gets called if the git-archive 'subst'
293 # variables were *not* expanded, and _version.py hasn't already been
294 # rewritten with a short version string, meaning we're inside a checked
297 # versions_from_git (as copied from python-versioneer) returns strings
298 # like "1.9.0-25-gb73aba9-dirty", which means we're in a tree with
299 # uncommited changes (-dirty), the latest checkin is revision b73aba9,
300 # the most recent tag was 1.9.0, and b73aba9 has 25 commits that weren't
301 # in 1.9.0 . The narrow-minded NormalizedVersion parser that takes our
302 # output (meant to enable sorting of version strings) refuses most of
303 # that. Tahoe uses a function named suggest_normalized_version() that can
304 # handle "1.9.0.post25", so dumb down our output to match.
307 source_dir = os.path.dirname(os.path.abspath(__file__))
309 # some py2exe/bbfreeze/non-CPython implementations don't do __file__
310 return {} # not always correct
312 if sys.platform == "win32":
314 stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"],
318 if not stdout.startswith(tag_prefix):
320 print "tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)
322 version = stdout[len(tag_prefix):]
323 pieces = version.split("-")
325 normalized_version = pieces[0]
327 normalized_version = "%s.post%s" % (pieces[0], pieces[1])
328 stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=source_dir)
331 full = stdout.strip()
332 if version.endswith("-dirty"):
334 normalized_version += ".dev0"
335 return {"version": version, "normalized": normalized_version, "full": full}
338 class UpdateVersion(Command):
339 description = "update _version.py from revision-control metadata"
342 def initialize_options(self):
344 def finalize_options(self):
347 target = self.distribution.versionfiles[0]
348 if os.path.isdir(os.path.join(basedir, "_darcs")):
349 verstr = self.try_from_darcs(target)
350 elif os.path.isdir(os.path.join(basedir, ".git")):
351 verstr = self.try_from_git(target)
353 print "no version-control data found, leaving _version.py alone"
356 self.distribution.metadata.version = verstr
358 def try_from_darcs(self, target):
359 from darcsver.darcsvermodule import update
360 (rc, verstr) = update(pkgname=self.distribution.get_name(),
361 verfilename=self.distribution.versionfiles,
362 revision_number=True,
363 version_body=DARCS_VERSION_BODY)
367 def try_from_git(self, target):
368 versions = versions_from_git("allmydata-tahoe-", verbose=True)
370 for fn in self.distribution.versionfiles:
372 f.write(GIT_VERSION_BODY %
373 { "pkgname": self.distribution.get_name(),
374 "version": versions["version"],
375 "normalized": versions["normalized"],
376 "full": versions["full"] })
378 print "git-version: wrote '%s' into '%s'" % (versions["version"], fn)
379 return versions.get("normalized", None)
382 class MySdist(sdist.sdist):
383 """ A hook in the sdist command so that we can determine whether this the
384 tarball should be 'SUMO' or not, i.e. whether or not to include the
385 external dependency tarballs. Note that we always include
386 misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
390 user_options = sdist.sdist.user_options + \
392 "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
394 boolean_options = ['sumo']
396 def initialize_options(self):
397 sdist.sdist.initialize_options(self)
400 def make_distribution(self):
401 # add our extra files to the list just before building the
402 # tarball/zipfile. We override make_distribution() instead of run()
403 # because setuptools.command.sdist.run() does not lend itself to
404 # easy/robust subclassing (the code we need to add goes right smack
405 # in the middle of a 12-line method). If this were the distutils
406 # version, we'd override get_file_list().
409 # If '--sumo' was specified, include tahoe-deps/* in the sdist.
410 # We assume that the user has fetched the tahoe-deps.tar.gz
411 # tarball and unpacked it already.
412 self.filelist.extend([os.path.join("tahoe-deps", fn)
413 for fn in os.listdir("tahoe-deps")])
414 # In addition, we want the tarball/zipfile to have -SUMO in the
415 # name, and the unpacked directory to have -SUMO too. The easiest
416 # way to do this is to patch self.distribution and override the
417 # get_fullname() method. (an alternative is to modify
418 # self.distribution.metadata.version, but that also affects the
419 # contents of PKG-INFO).
420 fullname = self.distribution.get_fullname()
422 return fullname + "-SUMO"
423 self.distribution.get_fullname = get_fullname
425 return sdist.sdist.make_distribution(self)
430 setup_args["version"] = version
433 description='secure, decentralized, fault-tolerant filesystem',
434 long_description=open('README.txt', 'rU').read(),
435 author='the Tahoe-LAFS project',
436 author_email='tahoe-dev@tahoe-lafs.org',
437 url='https://tahoe-lafs.org/',
438 license='GNU GPL', # see README.txt -- there is an alternative licence
439 cmdclass={"trial": Trial,
440 "make_executable": MakeExecutable,
441 "update_version": UpdateVersion,
444 package_dir = {'':'src'},
445 packages=['allmydata',
446 'allmydata.frontends',
447 'allmydata.immutable',
448 'allmydata.immutable.downloader',
449 'allmydata.introducer',
458 classifiers=trove_classifiers,
459 test_suite="allmydata.test",
460 install_requires=install_requires,
461 tests_require=tests_require,
462 package_data={"allmydata.web": ["*.xhtml", "*.js", "*.png", "*.css"],
464 setup_requires=setup_requires,
465 entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
466 zip_safe=False, # We prefer unzipped for easier access.
467 versionfiles=['src/allmydata/_version.py',],