]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
rewrite parts of the Makefile in setup.py. Add 'build_tahoe' and 'trial' subcommands.
authorBrian Warner <warner@allmydata.com>
Fri, 12 Sep 2008 01:03:21 +0000 (18:03 -0700)
committerBrian Warner <warner@allmydata.com>
Fri, 12 Sep 2008 01:03:21 +0000 (18:03 -0700)
The 'make build' target now runs 'setup.py build_tahoe', which figures out
where the target 'supportlib' directory should go, and invokes 'setup.py
develop' with the appropriate arguments.

The 'make test' target now runs 'setup.py trial', which manages sys.path and
runs trial as a subroutine instead of spawning an external process. This
simplifies the case where Twisted was built as a dependent library (and thus
the 'trial' executable is not on PATH).

setup.py now manages sys.path and PYTHONPATH for its internal subcommands, so
the $(PP) prefix was removed from all Makefile targets that invoke setup.py .
For the remaining ones, the 'setup.py -q show_pythonpath' subcommand was
added to compute this prefix with python rather than with fragile
shell/Makefile syntax.

Makefile
setup.py

index 94c5cb4c921579ebd9ddd4cd8819ff0a4ee50d75..1cce5538e7d7b939bfed4756722b60072d3c9fc2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,15 +4,9 @@
 default: build
 
 PYTHON=python
-PATHSEP=$(shell $(PYTHON) -c 'import os ; print os.pathsep')
-OSSEP=$(shell $(PYTHON) -c 'import os ; print os.sep')
 
-ifneq ($(INCLUDE_DIRS),)
-INCLUDE_DIRS_ARG = -I$(INCLUDE_DIRS)
-endif
-ifneq ($(LIBRARY_DIRS),)
-LIBRARY_DIRS_ARG = -L$(LIBRARY_DIRS)
-endif
+# setup.py will extend sys.path to include our support/lib/... directory
+# itself. It will also create it in the beginning of the 'develop' command.
 
 PLAT = $(strip $(shell $(PYTHON) -c "import sys ; print sys.platform"))
 ifeq ($(PLAT),win32)
@@ -22,20 +16,6 @@ ifeq ($(PLAT),win32)
        SRCPATH := $(shell cygpath -w $(shell pwd)/src)
        INNOSETUP := $(shell cygpath -au "$(PROGRAMFILES)/Inno Setup 5/Compil32.exe")
 else
-       ifeq ($(PLAT),linux2)
-               # This is to work-around #402, and anyway the poll reactor is probably better on Linux, if
-               # we have a lot of open fds.
-               ifeq ($(REACTOR),)
-                       REACTOR := poll
-               endif
-       endif
-       ifeq ($(PLAT),cygwin)
-               # The cygwin select reactor seems to run out of fds in unit tests -- it writes "filedescriptor
-               # out of range in select()".  Setting reactor=poll fixes that.
-               ifeq ($(REACTOR),)
-                       REACTOR := poll
-               endif
-       endif
        PYVER=$(shell $(PYTHON) misc/pyver.py)
        SUPPORT = $(shell pwd)/support
        SUPPORTLIB = $(SUPPORT)/lib/$(PYVER)/site-packages
@@ -44,28 +24,7 @@ else
        SITEDIRARG = --site-dirs=/var/lib/python-support/$(PYVER)
 endif
 
-ifneq ($(REACTOR),)
-       REACTOROPT := --reactor=$(REACTOR)
-else
-       REACTOROPT :=
-endif
-
-ifneq ($(PYTHONPATH),)
-        PP=PYTHONPATH="$(PYTHONPATH)$(PATHSEP)$(SUPPORTLIB)"
-else
-        PP=PYTHONPATH="$(SUPPORTLIB)"
-endif
-
-# Delete any quote chars in PATH.  Note that if the chars were escaped
-# (preceded by a back-slash) then the following subst will screw it up and
-# weird parsing errors will eventually result.
-
-PATH := $(subst ",,${PATH})
-PATH := $(subst ',,$(PATH))
-# ' "  # emacs syntax-highlighter gets confused by the bare quotes above
-
-TRIALCMD = $(shell PATH="$(PATH):${PWD}/support/bin" $(PP) $(PYTHON) misc/find_trial.py)
-TRIAL=PATH="$(PATH):${PWD}/support/bin" PYTHONUNBUFFERED=1 $(TRIALCMD) --rterrors $(REACTOROPT) $(TRIALOPT)
+PP=$(shell $(PYTHON) setup.py -q show_pythonpath)
 
 .PHONY: make-version build
 
@@ -73,7 +32,7 @@ TRIAL=PATH="$(PATH):${PWD}/support/bin" PYTHONUNBUFFERED=1 $(TRIALCMD) --rterror
 # http://pypi.python.org/pypi/darcsver It is necessary only if you want to
 # automatically produce a new _version.py file from the current darcs history.
 make-version:
-       $(PP) $(PYTHON) ./setup.py darcsver --count-all-patches
+       $(PYTHON) ./setup.py darcsver --count-all-patches
 
 # We want src/allmydata/_version.py to be up-to-date, but it's a fairly
 # expensive operation (about 6 seconds on a just-before-0.7.0 tree, probably
@@ -126,8 +85,7 @@ build: src/allmydata/_version.py
 # command line to work around this. Some day this will probably be fixed in
 # setuptools.
 build-once:
-       mkdir -p "$(SUPPORTLIB)"
-       $(PP) $(PYTHON) ./setup.py develop --prefix="$(SUPPORT)" $(SITEDIRARG)
+       $(PYTHON) setup.py build_tahoe
        chmod +x bin/tahoe
        touch .built
 
@@ -139,12 +97,10 @@ build-once:
 install: src/allmydata/_version.py
 ifdef PREFIX
        mkdir -p $(PREFIX)
-       $(PP) $(PYTHON) ./setup.py install \
-           --single-version-externally-managed \
+       $(PYTHON) ./setup.py install --single-version-externally-managed \
            --prefix=$(PREFIX) --record=./tahoe.files
 else
-       $(PP) $(PYTHON) ./setup.py install \
-           --single-version-externally-managed
+       $(PYTHON) ./setup.py install --single-version-externally-managed
 endif
 
 
@@ -177,18 +133,18 @@ TEST=allmydata
 # suppress the ansi color sequences
 
 test: build src/allmydata/_version.py
-       $(PP) $(TRIAL) $(TRIALARGS) $(TEST)
+       $(PYTHON) setup.py trial -a "$(TRIALARGS) $(TEST)"
 
 quicktest: .built .checked-deps
-       $(PP) $(TRIAL) $(TRIALARGS) $(TEST)
+       $(PYTHON) setup.py trial -a "$(TRIALARGS) $(TEST)"
 
 test-figleaf: build src/allmydata/_version.py
        rm -f .figleaf
-       $(PP) $(TRIAL) --reporter=bwverbose-figleaf $(TEST)
+       $(PYTHON) setup.py trial -a "--reporter=bwverbose-figleaf $(TEST)"
 
 quicktest-figleaf: src/allmydata/_version.py
        rm -f .figleaf
-       $(PP) $(TRIAL) --reporter=bwverbose-figleaf $(TEST)
+       $(PYTHON) setup.py trial -a "--reporter=bwverbose-figleaf $(TEST)"
 
 figleaf-output:
        $(PP) \
index 032765d07d0d2864ab6a13c7c74f7f885ae4fe4f..7f8c897498b710729f7d6bcd13998bc33b9d67c3 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,101 @@
 #
 # See the docs/about.html file for licensing information.
 
-import os, re, sys
+import os, re, sys, subprocess
+
+##### sys.path management
+
+basedir = os.path.dirname(os.path.abspath(__file__))
+pyver = "python%d.%d" % (sys.version_info[:2])
+if sys.platform == "win32":
+    supportlib = os.path.join("support", "Lib", "site-packages")
+else:
+    supportlib = os.path.join("support", "lib", pyver, "site-packages")
+supportlib = os.path.join(basedir, supportlib)
+
+def add_tahoe_paths():
+    """Modify sys.path and PYTHONPATH to include Tahoe and supporting libraries
+
+    The first step towards building Tahoe is to run::
+
+      python setup.py build_tahoe
+
+    which is the equivalent of::
+
+      mkdir -p $(BASEDIR)/support/lib/python2.5/site-packages
+       (or cygpath equivalent)
+      setup.py develop --prefix=$(BASEDIR)/support
+
+    This installs .eggs for any dependent libraries that aren't already
+    available on the system, into support/lib/pythonN.N/site-packages (or
+    support/Lib/site-packages on windows). It also adds an .egg-link for
+    Tahoe itself into the same directory.
+
+    We add this directory to os.environ['PYTHONPATH'], so that any child
+    processes we spawn will be able to use these packages.
+
+    When the setuptools site.py sees that supportlib in PYTHONPATH, it scans
+    through it for .egg and .egg-link entries, and adds them to sys.path .
+    Since python has already processed all the site.py files by the time we
+    get here, we perform this same sort of processing ourselves: this makes
+    tahoe (and dependency libraries) available to code within setup.py
+    itself. This is used by the 'setup.py trial' subcommand, which invokes
+    trial directly rather than spawning a subprocess (this is easier than
+    locating the 'trial' executable, especially when Twisted was installed as
+    a dependent library).
+
+    We'll need to add these .eggs to sys.path before importing anything that
+    isn't a part of stdlib. All the directories that we add this way are put
+    at the start of sys.path, so they will override anything that was present
+    on the system (and perhaps found lacking by the setuptools requirements
+    expressed in _auto_deps.py).
+    """
+
+    extra_syspath_items = []
+    extra_pythonpath_items = []
+
+    extra_syspath_items.append(supportlib)
+    extra_pythonpath_items.append(supportlib)
+
+    # Since we use setuptools to populate that directory, there will be a
+    # number of .egg and .egg-link entries there. Add all of them to
+    # sys.path, since that what the setuptools site.py would do if it
+    # encountered them at process start time. Without this step, the rest of
+    # this process would be unable to use the packages installed there. We
+    # don't need to add them to PYTHONPATH, since the site.py present there
+    # will add them when the child process starts up.
+
+    if os.path.isdir(supportlib):
+        for fn in os.listdir(supportlib):
+            if fn.endswith(".egg"):
+                extra_syspath_items.append(os.path.join(supportlib, fn))
+
+    # We also add our src/ directory, since that's where all the Tahoe code
+    # lives. This matches what site.py does when it sees the .egg-link file
+    # that is written to the support dir by an invocation of our 'setup.py
+    # develop' command.
+    extra_syspath_items.append(os.path.join(basedir, "src"))
+
+    # and we put an extra copy of everything from PYTHONPATH in front, so
+    # that it is possible to override the packages that setuptools downloads
+    # with alternate versions, by doing e.g. "PYTHONPATH=foo python setup.py
+    # trial"
+    oldpp = os.environ.get("PYTHONPATH", "").split(os.pathsep)
+    if oldpp == [""]:
+        # grr silly split() behavior
+        oldpp = []
+    extra_syspath_items = oldpp + extra_syspath_items
+
+    sys.path = extra_syspath_items + sys.path
+
+    # We also provide it to any child processes we spawn, via
+    # os.environ["PYTHONPATH"]
+    os.environ["PYTHONPATH"] = os.pathsep.join(oldpp + extra_pythonpath_items)
+
+# add_tahoe_paths() must be called before use_setuptools() is called. I don't
+# know why. If it isn't, then a later pkg_resources.requires(pycryptopp) call
+# fails because an old version (in /usr/lib) was already loaded.
+add_tahoe_paths()
 
 try:
     from ez_setup import use_setuptools
@@ -26,6 +120,7 @@ else:
 
 from setuptools import Extension, find_packages, setup
 from setuptools.command import sdist
+from distutils.core import Command
 
 # Make the dependency-version-requirement, which is used by the Makefile at
 # build-time, also available to the app at runtime:
@@ -121,6 +216,111 @@ if 'darcsver' in sys.argv[1:]:
 if not os.path.exists('PKG-INFO'):
     setup_requires.append('setuptools_darcs >= 1.1.0')
 
+class ShowSupportLib(Command):
+    user_options = []
+    def initialize_options(self):
+        pass
+    def finalize_options(self):
+        pass
+    def run(self):
+        # TODO: --quiet suppresses the 'running show_supportlib' message.
+        # Find a way to do this all the time.
+        print supportlib # TODO windowsy
+
+class ShowPythonPath(Command):
+    user_options = []
+    def initialize_options(self):
+        pass
+    def finalize_options(self):
+        pass
+    def run(self):
+        # TODO: --quiet suppresses the 'running show_supportlib' message.
+        # Find a way to do this all the time.
+        print "PYTHONPATH=%s" % os.environ["PYTHONPATH"]
+
+class BuildTahoe(Command):
+    user_options = []
+    def initialize_options(self):
+        pass
+    def finalize_options(self):
+        pass
+    def run(self):
+        # 'setup.py develop --prefix SUPPORT' will complain if SUPPORTLIB is
+        # not on PYTHONPATH, because it thinks you are installing to a place
+        # that will not be searched at runtime (which is true, except that we
+        # add SUPPORTLIB to PYTHONPATH to run tests, etc). So set up
+        # PYTHONPATH now, then spawn a 'setup.py develop' command. Also, we
+        # have to create the directory ourselves.
+        if not os.path.isdir(supportlib):
+            os.makedirs(supportlib)
+
+        command = [sys.executable, "setup.py", "develop", "--prefix", "support"]
+        if sys.platform == "linux2":
+            # workaround for tahoe #229 / setuptools #17, on debian
+            command.extend(["--site-dirs", "/var/lib/python-support/" + pyver])
+        print "Command:", " ".join(command)
+        rc = subprocess.call(command)
+        if rc < 0:
+            print >>sys.stderr, "'setup.py develop' terminated by signal", -rc
+            sys.exit(1)
+        elif rc > 0:
+            print >>sys.stderr, "'setup.py develop' exited with rc", rc
+            sys.exit(rc)
+
+class Trial(Command):
+    # Unlike 'build' and 'bdist_egg', the 'trial' subcommand cannot be run in
+    # conjunction with other subcommands.
+
+    # The '-a' argument is split on whitespace and passed into trial. (the
+    # distutils parser does not give subcommands access to the rest of
+    # sys.argv, so unfortunately we cannot just do something like:
+    #   setup.py trial --reporter=text allmydata.test.test_util
+
+    # Examples:
+    #  setup.py trial    # run all tests
+    #  setup.py trial -a allmydata.test.test_util   # run some tests
+    #  setup.py trial -a '--reporter=text allmydata.test.test_util' #other args
+
+    description = "Run unit tests via trial"
+
+    user_options = [ ("args=", "a", "Argument string to pass to trial: setup.py trial -a allmydata.test.test_util"),
+                     ]
+    def initialize_options(self):
+        self.args = "allmydata"
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        # make sure Twisted is available (for trial itself), and both the
+        # Tahoe source code and our dependent libraries are available (so
+        # that trial has some test code to work with)
+
+        from twisted.scripts import trial
+
+        args = self.args.strip().split()
+
+        # one wrinkle: we want to set the reactor here, because of bug #402
+        # (twisted bug #3218). We just jam in a "--reactor poll" at the start
+        # of the arglist. This does not permit the reactor to be overridden,
+        # unfortunately.
+        if sys.platform in ("linux2", "cygwin"):
+            # poll on linux2 to avoid #402 problems with select
+            # poll on cygwin since selectreactor runs out of fds
+            args = ["--reactor", "poll"] + args
+
+        # zooko also had os.environ["PYTHONUNBUFFERED"]="1" and
+        # args.append("--rterrors")
+
+        sys.argv = ["trial"] + args
+        if self.verbose > 1:
+            print "To run this test directly, use:"
+            print "PYTHONPATH=%s %s" % (os.environ["PYTHONPATH"],
+                                        " ".join(sys.argv))
+        else:
+            print "(run with -vv for trial command-line details)"
+        trial.run() # this does sys.exit
+        # NEVER REACHED
+
 class MySdist(sdist.sdist):
     """ A hook in the sdist command so that we can determine whether this the
     tarball should be 'SUMO' or not, i.e. whether or not to include the
@@ -160,7 +360,9 @@ class MySdist(sdist.sdist):
             if data not in dist_files:
                 dist_files.append(data)
 
-import _auto_deps
+# get a list of the libraries that we depend upon, for use in the call to
+# setup() at the end of this file
+from _auto_deps import install_requires
 
 setup(name='allmydata-tahoe',
       version=verstr,
@@ -170,12 +372,17 @@ setup(name='allmydata-tahoe',
       author_email='tahoe-dev@allmydata.org',
       url='http://allmydata.org/',
       license='GNU GPL',
-      cmdclass={'sdist': MySdist},
+      cmdclass={"show_supportlib": ShowSupportLib,
+                "show_pythonpath": ShowPythonPath,
+                "build_tahoe": BuildTahoe,
+                "trial": Trial,
+                "sdist": MySdist,
+                },
       package_dir = {'':'src'},
       packages=find_packages("src"),
       classifiers=trove_classifiers,
       test_suite="allmydata.test",
-      install_requires=_auto_deps.install_requires,
+      install_requires=install_requires,
       include_package_data=True,
       setup_requires=setup_requires,
       dependency_links=dependency_links,