]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - setup.py
Attempt to fix test-with-fake-dist build step.
[tahoe-lafs/tahoe-lafs.git] / setup.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # Tahoe-LAFS -- secure, distributed storage grid
5 #
6 # Copyright © 2008-2010 Allmydata, Inc.
7 #
8 # This file is part of Tahoe-LAFS.
9 #
10 # See the docs/about.html file for licensing information.
11
12 import glob, os, shutil, stat, subprocess, sys, zipfile, re
13
14 ##### sys.path management
15
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")
20     else:
21         return os.path.join(prefixdir, "lib", pyver, "site-packages")
22
23 basedir = os.path.dirname(os.path.abspath(__file__))
24 supportlib = pylibdir(os.path.join(basedir, "support"))
25
26 # locate our version number
27
28 def read_version_py(infname):
29     try:
30         verstrline = open(infname, "rt").read()
31     except EnvironmentError:
32         return None
33     else:
34         VSRE = r"^verstr = ['\"]([^'\"]*)['\"]"
35         mo = re.search(VSRE, verstrline, re.M)
36         if mo:
37             return mo.group(1)
38
39 version = read_version_py("src/allmydata/_version.py")
40
41 APPNAME='allmydata-tahoe'
42 APPNAMEFILE = os.path.join('src', 'allmydata', '_appname.py')
43 APPNAMEFILESTR = "__appname__ = '%s'" % (APPNAME,)
44 try:
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)
49 else:
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)
52         sys.exit(-1)
53
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:
58
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()
61 # below.
62 adglobals = {}
63 execfile('src/allmydata/_auto_deps.py', adglobals)
64 install_requires = adglobals['install_requires']
65
66 if len(sys.argv) > 1 and sys.argv[1] == '--fakedependency':
67     del sys.argv[1]
68     install_requires += ["fakedependency >= 1.0.0"]
69
70 __requires__ = install_requires[:]
71 if 'trial' in sys.argv or 'test' in sys.argv:
72     if version is not None:
73         __requires__.append(APPNAME + '==' + version)
74     else:
75         __requires__.append(APPNAME)
76
77 egg = os.path.realpath(glob.glob('setuptools-*.egg')[0])
78 sys.path.insert(0, egg)
79 egg = os.path.realpath(glob.glob('darcsver-*.egg')[0])
80 sys.path.insert(0, egg)
81 import setuptools; setuptools.bootstrap_install_from = egg
82
83 from setuptools import find_packages, setup
84 from setuptools.command import sdist
85 from setuptools import Command
86
87 trove_classifiers=[
88     "Development Status :: 5 - Production/Stable",
89     "Environment :: Console",
90     "Environment :: Web Environment",
91     "License :: OSI Approved :: GNU General Public License (GPL)",
92     "License :: DFSG approved",
93     "License :: Other/Proprietary License",
94     "Intended Audience :: Developers",
95     "Intended Audience :: End Users/Desktop",
96     "Intended Audience :: System Administrators",
97     "Operating System :: Microsoft",
98     "Operating System :: Microsoft :: Windows",
99     "Operating System :: Microsoft :: Windows :: Windows NT/2000",
100     "Operating System :: Unix",
101     "Operating System :: POSIX :: Linux",
102     "Operating System :: POSIX",
103     "Operating System :: MacOS :: MacOS X",
104     "Operating System :: OS Independent",
105     "Natural Language :: English",
106     "Programming Language :: C",
107     "Programming Language :: Python",
108     "Programming Language :: Python :: 2",
109     "Programming Language :: Python :: 2.4",
110     "Programming Language :: Python :: 2.5",
111     "Programming Language :: Python :: 2.6",
112     "Programming Language :: Python :: 2.7",
113     "Topic :: Utilities",
114     "Topic :: System :: Systems Administration",
115     "Topic :: System :: Filesystems",
116     "Topic :: System :: Distributed Computing",
117     "Topic :: Software Development :: Libraries",
118     "Topic :: Communications :: Usenet News",
119     "Topic :: System :: Archiving :: Backup",
120     "Topic :: System :: Archiving :: Mirroring",
121     "Topic :: System :: Archiving",
122     ]
123
124
125 setup_requires = []
126
127 # The darcsver command from the darcsver plugin is needed to initialize the
128 # distribution's .version attribute correctly. (It does this either by
129 # examining darcs history, or if that fails by reading the
130 # src/allmydata/_version.py file). darcsver will also write a new version
131 # stamp in src/allmydata/_version.py, with a version number derived from
132 # darcs history. Note that the setup.cfg file has an "[aliases]" section
133 # which enumerates commands that you might run and specifies that it will run
134 # darcsver before each one. If you add different commands (or if I forgot
135 # some that are already in use), you may need to add it to setup.cfg and
136 # configure it to run darcsver before your command, if you want the version
137 # number to be correct when that command runs.
138 # http://pypi.python.org/pypi/darcsver
139 setup_requires.append('darcsver >= 1.7.1')
140
141 # Nevow requires Twisted to setup, but doesn't declare that requirement in a
142 # way that enables setuptools to satisfy that requirement before Nevow's
143 # setup.py tried to "import twisted". Fortunately we require setuptools_trial
144 # to setup and setuptools_trial requires Twisted to install, so hopefully
145 # everything will work out until the Nevow issue is fixed:
146 # http://divmod.org/trac/ticket/2629 setuptools_trial is needed if you want
147 # "./setup.py trial" or "./setup.py test" to execute the tests (and in order
148 # to make sure Twisted is installed early enough -- see the paragraph above).
149 # http://pypi.python.org/pypi/setuptools_trial
150 setup_requires.extend(['setuptools_trial >= 0.5'])
151
152 # setuptools_darcs is required to produce complete distributions (such as
153 # with "sdist" or "bdist_egg") (unless there is a PKG-INFO file present which
154 # shows that this is itself a source distribution). For simplicity, and
155 # because there is some unknown error with setuptools_darcs when building and
156 # testing tahoe all in one python command on some platforms, we always add it
157 # to setup_requires. http://pypi.python.org/pypi/setuptools_darcs
158 setup_requires.append('setuptools_darcs >= 1.1.0')
159
160 # trialcoverage is required if you want the "trial" unit test runner to have a
161 # "--reporter=bwverbose-coverage" option which produces code-coverage results.
162 # The required version is 0.3.3, because that is the latest version that only
163 # depends on a version of pycoverage for which binary packages are available.
164 if "--reporter=bwverbose-coverage" in sys.argv:
165     setup_requires.append('trialcoverage >= 0.3.3')
166
167 # stdeb is required to produce Debian files with the "sdist_dsc" command.
168 if "sdist_dsc" in sys.argv:
169     setup_requires.append('stdeb >= 0.3')
170
171 tests_require=[
172     # Mock - Mocking and Testing Library
173     # http://www.voidspace.org.uk/python/mock/
174     "mock",
175     ]
176
177 class ShowSupportLib(Command):
178     user_options = []
179     def initialize_options(self):
180         pass
181     def finalize_options(self):
182         pass
183     def run(self):
184         # TODO: --quiet suppresses the 'running show_supportlib' message.
185         # Find a way to do this all the time.
186         print supportlib # TODO windowsy
187
188 class ShowPythonPath(Command):
189     user_options = []
190     def initialize_options(self):
191         pass
192     def finalize_options(self):
193         pass
194     def run(self):
195         # TODO: --quiet suppresses the 'running show_supportlib' message.
196         # Find a way to do this all the time.
197         print "PYTHONPATH=%s" % os.environ.get("PYTHONPATH", '')
198
199 class RunWithPythonPath(Command):
200     description = "Run a subcommand with PYTHONPATH set appropriately"
201
202     user_options = [ ("python", "p",
203                       "Treat command string as arguments to a python executable"),
204                      ("command=", "c", "Command to be run"),
205                      ("directory=", "d", "Directory to run the command in"),
206                      ]
207     boolean_options = ["python"]
208
209     def initialize_options(self):
210         self.command = None
211         self.python = False
212         self.directory = None
213     def finalize_options(self):
214         pass
215     def run(self):
216         oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
217         if oldpp == [""]:
218             # grr silly split() behavior
219             oldpp = []
220         os.environ['PYTHONPATH'] = os.pathsep.join(oldpp + [supportlib,])
221
222         # We must require the command to be safe to split on
223         # whitespace, and have --python and --directory to make it
224         # easier to achieve this.
225
226         command = []
227         if self.python:
228             command.append(sys.executable)
229         if self.command:
230             command.extend(self.command.split())
231         if not command:
232             raise RuntimeError("The --command argument is mandatory")
233         if self.directory:
234             os.chdir(self.directory)
235         if self.verbose:
236             print "command =", " ".join(command)
237         rc = subprocess.call(command)
238         sys.exit(rc)
239
240 class TestMacDiskImage(Command):
241     user_options = []
242     def initialize_options(self):
243         pass
244     def finalize_options(self):
245         pass
246     def run(self):
247         import sys
248         sys.path.append(os.path.join('misc', 'build_helpers'))
249         import test_mac_diskimage
250         return test_mac_diskimage.test_mac_diskimage('Allmydata', version=self.distribution.metadata.version)
251
252 class CheckAutoDeps(Command):
253     user_options = []
254     def initialize_options(self):
255         pass
256     def finalize_options(self):
257         pass
258     def run(self):
259         adglobals = {}
260         execfile('src/allmydata/_auto_deps.py', adglobals)
261         adglobals['require_auto_deps']()
262
263
264 class MakeExecutable(Command):
265     user_options = []
266     def initialize_options(self):
267         pass
268     def finalize_options(self):
269         pass
270     def run(self):
271         bin_tahoe_template = os.path.join("bin", "tahoe-script.template")
272
273         if sys.platform == 'win32':
274             # 'tahoe' script is needed for cygwin
275             script_names = ["tahoe.pyscript", "tahoe"]
276         else:
277             script_names = ["tahoe"]
278
279         # Create the tahoe script file under the 'bin' directory. This
280         # file is exactly the same as the 'tahoe-script.template' script
281         # except that the shebang line is rewritten to use our sys.executable
282         # for the interpreter.
283         f = open(bin_tahoe_template, "rU")
284         script_lines = f.readlines()
285         f.close()
286         script_lines[0] = '#!%s\n' % (sys.executable,)
287         for script_name in script_names:
288             tahoe_script = os.path.join("bin", script_name)
289             try:
290                 os.remove(tahoe_script)
291             except Exception:
292                 if os.path.exists(tahoe_script):
293                    raise
294             f = open(tahoe_script, "wb")
295             for line in script_lines:
296                 f.write(line)
297             f.close()
298
299             # chmod +x
300             old_mode = stat.S_IMODE(os.stat(tahoe_script)[stat.ST_MODE])
301             new_mode = old_mode | (stat.S_IXUSR | stat.S_IRUSR |
302                                    stat.S_IXGRP | stat.S_IRGRP |
303                                    stat.S_IXOTH | stat.S_IROTH )
304             os.chmod(tahoe_script, new_mode)
305
306         old_tahoe_exe = os.path.join("bin", "tahoe.exe")
307         try:
308             os.remove(old_tahoe_exe)
309         except Exception:
310             if os.path.exists(old_tahoe_exe):
311                 raise
312
313
314 class MySdist(sdist.sdist):
315     """ A hook in the sdist command so that we can determine whether this the
316     tarball should be 'SUMO' or not, i.e. whether or not to include the
317     external dependency tarballs. Note that we always include
318     misc/dependencies/* in the tarball; --sumo controls whether tahoe-deps/*
319     is included as well.
320     """
321
322     user_options = sdist.sdist.user_options + \
323         [('sumo', 's',
324           "create a 'sumo' sdist which includes the contents of tahoe-deps/*"),
325          ]
326     boolean_options = ['sumo']
327
328     def initialize_options(self):
329         sdist.sdist.initialize_options(self)
330         self.sumo = False
331
332     def make_distribution(self):
333         # add our extra files to the list just before building the
334         # tarball/zipfile. We override make_distribution() instead of run()
335         # because setuptools.command.sdist.run() does not lend itself to
336         # easy/robust subclassing (the code we need to add goes right smack
337         # in the middle of a 12-line method). If this were the distutils
338         # version, we'd override get_file_list().
339
340         if self.sumo:
341             # If '--sumo' was specified, include tahoe-deps/* in the sdist.
342             # We assume that the user has fetched the tahoe-deps.tar.gz
343             # tarball and unpacked it already.
344             self.filelist.extend([os.path.join("tahoe-deps", fn)
345                                   for fn in os.listdir("tahoe-deps")])
346             # In addition, we want the tarball/zipfile to have -SUMO in the
347             # name, and the unpacked directory to have -SUMO too. The easiest
348             # way to do this is to patch self.distribution and override the
349             # get_fullname() method. (an alternative is to modify
350             # self.distribution.metadata.version, but that also affects the
351             # contents of PKG-INFO).
352             fullname = self.distribution.get_fullname()
353             def get_fullname():
354                 return fullname + "-SUMO"
355             self.distribution.get_fullname = get_fullname
356
357         return sdist.sdist.make_distribution(self)
358
359 setup_args = {}
360 if version:
361     setup_args["version"] = version
362
363 setup(name=APPNAME,
364       description='secure, decentralized, fault-tolerant filesystem',
365       long_description=open('README.txt', 'rU').read(),
366       author='the Tahoe-LAFS project',
367       author_email='tahoe-dev@tahoe-lafs.org',
368       url='http://tahoe-lafs.org/',
369       license='GNU GPL', # see README.txt -- there is an alternative licence
370       cmdclass={"show_supportlib": ShowSupportLib,
371                 "show_pythonpath": ShowPythonPath,
372                 "run_with_pythonpath": RunWithPythonPath,
373                 "check_auto_deps": CheckAutoDeps,
374                 "test_mac_diskimage": TestMacDiskImage,
375                 "make_executable": MakeExecutable,
376                 "sdist": MySdist,
377                 },
378       package_dir = {'':'src'},
379       packages=find_packages("src"),
380       classifiers=trove_classifiers,
381       test_suite="allmydata.test",
382       install_requires=install_requires,
383       tests_require=tests_require,
384       include_package_data=True,
385       setup_requires=setup_requires,
386       entry_points = { 'console_scripts': [ 'tahoe = allmydata.scripts.runner:run' ] },
387       zip_safe=False, # We prefer unzipped for easier access.
388       versionfiles=['src/allmydata/_version.py',],
389       **setup_args
390       )