[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: Merging Ctypes Python Branch

From: David James <james_at_cs.toronto.edu>
Date: 2007-09-01 02:10:23 CEST

On 8/30/07, Eric Gillespie <epg@pretzelnet.org> wrote:
> "Sage La Torra" <sagelt@gmail.com> writes:
>
> > As of last week, David James and I think that the Ctypes Python
> > Binding Branch is ready for merging, or at least a thorough review in
> > preparation for merging. The branch is available at
> > http://svn.collab.net/repos/svn/branches/ctypes-python-bindings/.
>
> As a first step to reviewing, I tried to build it. Currently,
> you assume everything is installed on the linker search path, but
> this is frequently not the case. So, I changed autogen.py and
> ctypesgen/wrap.py to take -L, -R, and --rpath options (as well as
> the -Wl,-prefixed forms) to specify libdirs to search.
>
> I also changed autogen.py to use apr-1-config --ldflags --link-ld
> so that ensuring autogen.py has the right apr-1-config is all a
> user needs to do if all svn stuff is in the same libdir.

Hi Eric,

Currently, autogen.py assumes that both apr and apr-util are installed
under the same prefix, according to the output of apr-1-config
--prefix. Further, you can specify where Subversion is installed by
using the "--prefix" option for autogen.py.

We add these directories to the default linker path in autogen.py by
setting the LIBRARY_PATH environment variable.

On my system, Subversion is installed in $HOME. APR is installed in
/usr/local/apache2. To configure the ctypes Python bindings, I run the
following command:
   python autogen.py --apr-config=/usr/local/apache2/bin/apr-1-config -p $HOME

Unfortunately, this didn't work, because of a bug in the ctypes
find_library function (the find_library function doesn't strip colons
from the output of gcc -Wl,-t correctly!). I patched wraptypes to work
around this bug and the bindings build like a charm without any other
changes.

Could you try the patched version of ctypesgen on your system and see
if it helps? You'll need to first update ctypesgen by typing ( cd
ctypesgen && svn up ). I'm particularly interested to see if this
works on OS X, which has a different linker and might require us to
play with different options.

Cheers,

David

P.S. If my patch solves your problem, are there still parts of your
patch that you'd still like me to commit? Let me know.

>
> [[[
> * autogen.py
> (option_W): Add optparse callback to parse -Wl,-prefixed linker
> options (typically used as -Wl,-R/some/dir to specify RPATH).
> (parser): Handle -L, -R, --rpath, and -W (using option_W) options,
> storing values into options.libdirs list.
> (get_apr_config): Initialize ldflags to the output of 'apr-1-config
> --ldflags --libs' so we pick up any libdir options, to be passed on to
> ctypesgen/wrap.py .
> Pass options.libdirs on to ctypesgen/wrap.py with -R option. Handle
> errors from ctypesgen/wrap.py .
>
> * wrap.py
> (load_library): Take new optional libdirs argument; if provided, if CDLL
> couldn't find the library, and if the path from find_library is
> relative, try loading the library from each of the libdirs.
> (CtypesWrapper.begin_output): Save new optional libdirs argument in
> self.libdirs for foo.
> (CtypesWrapper.print_preamble): Pass self.libdirs along to load_library.
> (option_W): Add optparse callback to parse -Wl,-prefixed linker
> options (typically used as -Wl,-R/some/dir to specify RPATH).
> (main): Handle -L, -R, --rpath, and -W (using option_W) options, storing
> values into options.libdirs list.
> ]]]
>
> Index: autogen.py
> ===================================================================
> --- autogen.py (revision 26401)
> +++ autogen.py (working copy)
> @@ -6,6 +6,17 @@
> from tempfile import mkdtemp
> from glob import glob
>
> +def option_W(option, opt, value, parser):
> + if len(value) < 4 or value[0:3] != 'l,-':
> + raise optparse.BadOptionError("not in '-Wl,<opt>' form: %s%s"
> + % (opt, value))
> + opt = value[2:]
> + if opt not in ['-L', '-R', '--rpath']:
> + raise optparse.BadOptionError("-Wl option must be -L, -R"
> + " or --rpath, not " + value[2:])
> + # Push the linker option onto the list for further parsing.
> + parser.rargs.insert(0, value)
> +
> parser = OptionParser()
> parser.add_option("-a", "--apr-config", dest="apr_config",
> help="The full path to your apr-1-config or apr-config script")
> @@ -15,6 +26,12 @@
> parser.add_option("", "--save-preprocessed-headers", dest="filename",
> help="Save the preprocessed headers to the specified "
> "FILENAME")
> +parser.add_option("-W", action="callback", callback=option_W, type='str',
> + metavar="l,OPTION",
> + help="where OPTION is -L, -R, or --rpath")
> +parser.add_option("-L", "-R", "--rpath", action="append", dest="libdirs",
> + metavar="LIBDIR", help="Add LIBDIR to the search path")
> +parser.set_defaults(libdirs=[])
>
> (options, args) = parser.parse_args()
>
> @@ -48,6 +65,10 @@
> apr_include_dir = run_cmd("%s --includedir" % apr_config).strip()
> apr_version = run_cmd("%s --version" % apr_config).strip()
> cpp = run_cmd("%s --cpp" % apr_config).strip()
> +
> + fout = run_cmd("%s --ldflags --link-ld" % apr_config)
> + if fout:
> + ldflags = fout.split()
> break
> else:
> print ferr
> @@ -72,10 +93,9 @@
> "to your Subversion installation using the --prefix\n"
> "option.")
>
> - ldflags = [
> - "-lapr-%s" % apr_version[0],
> - "-laprutil-%s" % apr_version[0],
> - ]
> + # TODO: Use apu-1-config, as above:
> + # ldflags.extend(run_cmd('apu-1-config --ldflags --link-ld').split())
> + ldflags.append("-laprutil-%s" % apr_version[0])
>
> # List the libraries in the order they should be loaded
> libraries = [
> @@ -124,16 +144,31 @@
>
> os.environ["LIBRARY_PATH"] = library_path
>
> -cmd = ("cd %s && %s %s/ctypesgen/wrap.py --cpp '%s %s' %s "
> +cmd = ["cd %s && %s %s/ctypesgen/wrap.py --cpp '%s %s' %s "
> "%s -o svn_all.py" % (tempdir, sys.executable, os.getcwd(),
> - cpp, flags, ldflags, includes))
> + cpp, flags, ldflags, includes)]
> +cmd.extend('-R ' + x for x in options.libdirs)
> +cmd = ' '.join(cmd)
>
> if options.filename:
> cmd += " --save-preprocessed-headers=%s" % \
> os.path.abspath(options.filename)
>
> print cmd
> -os.system(cmd)
> +status = os.system(cmd)
> +if os.WIFEXITED(status):
> + status = os.WEXITSTATUS(status)
> + if status != 0:
> + sys.exit(status)
> +elif os.WIFSIGNALED(status):
> + sys.stderr.write("wrap.py killed with signal %d" % (os.WTERMSIG(status),))
> + sys.exit(2)
> +elif os.WIFSTOPPED(status):
> + sys.stderr.write("wrap.py stopped with signal %d" % (os.WSTOPSIG(status),))
> + sys.exit(2)
> +else:
> + sys.stderr.write("wrap.py exited with invalid status %d" % (status,))
> + sys.exit(2)
>
> func_re = re.compile(r"CFUNCTYPE\(POINTER\((\w+)\)")
> out = file("%s/svn_all2.py" % tempdir, "w")
> Index: ctypesgen/wrap.py
> ===================================================================
> --- ctypesgen/wrap.py (revision 35)
> +++ ctypesgen/wrap.py (working copy)
> @@ -14,11 +14,12 @@
>
> from ctypesparser import *
> import textwrap
> -import sys, re
> +import optparse, os, sys, re
> +from errno import ENOENT
> from ctypes import CDLL, RTLD_GLOBAL, c_byte
> from ctypes.util import find_library
>
> -def load_library(name, mode=RTLD_GLOBAL):
> +def load_library(name, mode=RTLD_GLOBAL, libdirs=None):
> if os.name == "nt":
> return CDLL(name, mode=mode)
> path = find_library(name)
> @@ -26,14 +27,34 @@
> # Maybe 'name' is not a library name in the linker style,
> # give CDLL a last chance to find the library.
> path = name
> - return CDLL(path, mode=mode)
> + try:
> + return CDLL(path, mode=mode)
> + except OSError, e:
> + # XXX Sadly, ctypes raises OSError without setting errno.
> + if e.errno not in [ENOENT, None]:
> + raise
> + if os.path.isabs(path) or libdirs is None:
> + raise
> + # path is relative and the linker couldn't find it; if the
> + # user provided any libdirs, try those.
> + # XXX This isn't quite right; if the user provided libdirs, we
> + # should *only* try those; he may be specifically trying to
> + # avoid one on the system search path. But we're relying on
> + # find_library to turn e.g. 'apr-1' into 'libapr-1.so.0'
> + for libdir in set(libdirs):
> + try:
> + return CDLL(os.path.join(libdir, path), mode=mode)
> + except OSError, e:
> + if e.errno not in [ENOENT, None]:
> + raise
>
> class CtypesWrapper(CtypesParser, CtypesTypeVisitor):
> file=None
> def begin_output(self, output_file, library, link_modules=(),
> emit_filenames=(), all_headers=False,
> include_symbols=None, exclude_symbols=None,
> - save_preprocessed_headers=None):
> + save_preprocessed_headers=None,
> + libdirs=None):
> self.library = library
> self.file = output_file
> self.all_names = []
> @@ -51,6 +72,7 @@
> self.exclude_symbols = re.compile(exclude_symbols)
> self.preprocessor_parser.save_preprocessed_headers = \
> save_preprocessed_headers
> + self.libdirs = libdirs
>
> self.linked_symbols = {}
> for name in link_modules:
> @@ -180,7 +202,7 @@
> }).lstrip()
> self.loaded_libraries = []
> for library in self.library:
> - lib = load_library(library)
> + lib = load_library(library, libdirs=self.libdirs)
> if lib:
> self.loaded_libraries.append(lib)
> print >>self.file, textwrap.dedent("""
> @@ -314,11 +336,19 @@
> self.all_names.append(name)
> break
>
> +def option_W(option, opt, value, parser):
> + if len(value) < 4 or value[0:3] != 'l,-':
> + raise optparse.BadOptionError("not in '-Wl,<opt>' form: %s%s"
> + % (opt, value))
> + opt = value[2:]
> + if opt not in ['-L', '-R', '--rpath']:
> + raise optparse.BadOptionError("-Wl option must be -L, -R"
> + " or --rpath, not " + value[2:])
> + # Push the linker option onto the list for further parsing.
> + parser.rargs.insert(0, value)
> +
> def main(*argv):
> from tempfile import NamedTemporaryFile
> - import optparse
> - import sys
> - import os.path
>
> usage = 'usage: %prog [options] <header.h>'
> op = optparse.OptionParser(usage=usage)
> @@ -341,7 +371,12 @@
> op.add_option('', '--save-preprocessed-headers', dest='filename',
> help='Save the preprocessed headers to the specified '
> 'FILENAME')
> -
> + op.add_option("-W", action="callback", callback=option_W, type='str',
> + metavar="l,OPTION",
> + help="where OPTION is -L, -R, or --rpath")
> + op.add_option("-L", "-R", "--rpath", action="append", dest="libdirs",
> + metavar="LIBDIR", help="Add LIBDIR to the search path")
> +
> (options, args) = op.parse_args(list(argv[1:]))
> if len(args) < 1:
> print >> sys.stderr, 'No header files specified.'
> @@ -363,7 +398,8 @@
> all_headers=options.all_headers,
> include_symbols=options.include_symbols,
> exclude_symbols=options.exclude_symbols,
> - save_preprocessed_headers=options.filename
> + save_preprocessed_headers=options.filename,
> + libdirs=options.libdirs,
> )
> wrapper.preprocessor_parser.cpp = options.cpp
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
> For additional commands, e-mail: dev-help@subversion.tigris.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Sep 1 02:07:33 2007

This is an archived mail posted to the Subversion Dev mailing list.