diff options
author | Owen W. Taylor <otaylor@fishsoup.net> | 2009-08-13 16:09:27 -0400 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2009-08-17 00:11:44 -0400 |
commit | af6bcb48d777f683384d9b8497e1b0edba5b16e7 (patch) | |
tree | c80ce74f9f0611dedb9d21e9d15176a269ceb69b /giscanner | |
parent | 19db6454c48b6d8e4a0f033cf6c1d2b4be66f6c7 (diff) | |
download | gobject-introspection-af6bcb48d777f683384d9b8497e1b0edba5b16e7.tar.gz |
Resolve library names to shared libraries ourselves
Using ctypes.util.find_library() to resolve library names to
sonames causes problems with dealing with uninstalled libtool
operation properly. We're unlikely to find any way of combining
the two that will be robust against future changes in both
facilities.
Switch to a different approach - run 'ldd' on the compiled
introspection binary and extract sonames from there This is
less portable but should be quite robust where it works.
utils.py dumper.py: Move libtool-command-line finding into utils.py
girwriter.py: Remove library name resolution from here, expect libraries
to be passed in preresolved.
shlibs.py scannermain.py: New file including resolve_shlibs() to resolve
library names using the introspection binary.
tests/scanner/Makefile.am: Add .libs to LD_LIBRARY_PATH
http://bugzilla.gnome.org/show_bug.cgi?id=591669
Diffstat (limited to 'giscanner')
-rw-r--r-- | giscanner/Makefile.am | 1 | ||||
-rw-r--r-- | giscanner/dumper.py | 24 | ||||
-rw-r--r-- | giscanner/girwriter.py | 10 | ||||
-rw-r--r-- | giscanner/scannermain.py | 5 | ||||
-rw-r--r-- | giscanner/shlibs.py | 77 | ||||
-rw-r--r-- | giscanner/utils.py | 22 |
6 files changed, 107 insertions, 32 deletions
diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am index 1ecd9778..098235b4 100644 --- a/giscanner/Makefile.am +++ b/giscanner/Makefile.am @@ -47,6 +47,7 @@ pkgpyexec_PYTHON = \ libtoolimporter.py \ minixpath.py \ odict.py \ + shlibs.py \ scannermain.py \ sourcescanner.py \ transformer.py \ diff --git a/giscanner/dumper.py b/giscanner/dumper.py index 622b2522..b8f16c9d 100644 --- a/giscanner/dumper.py +++ b/giscanner/dumper.py @@ -24,6 +24,7 @@ import subprocess import tempfile from .glibtransformer import IntrospectionBinary +from .utils import get_libtool_command # bugzilla.gnome.org/558436 # Compile a binary program which is then linked to a library @@ -146,27 +147,6 @@ class DumpCompiler(object): stdout=subprocess.PIPE) return proc.communicate()[0].split() - def _use_libtool_infection(self): - libtool_infection = not self._options.nolibtool - if not libtool_infection: - return None - - libtool_path = self._options.libtool_path - if libtool_path: - # Automake by default sets: - # LIBTOOL = $(SHELL) $(top_builddir)/libtool - # To be strictly correct we would have to parse shell. For now - # we simply split(). - return libtool_path.split(' ') - - try: - subprocess.check_call(['libtool', '--version']) - except subprocess.CalledProcessError, e: - # If libtool's not installed, assume we don't need it - return None - - return ['libtool'] - def _compile(self, output, *sources): # Not strictly speaking correct, but easier than parsing shell args = self._compiler_cmd.split() @@ -189,7 +169,7 @@ class DumpCompiler(object): def _link(self, output, *sources): args = [] - libtool = self._use_libtool_infection() + libtool = get_libtool_command(self._options) if libtool: args.extend(libtool) args.append('--mode=link') diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py index ddd562cd..aad55ef1 100644 --- a/giscanner/girwriter.py +++ b/giscanner/girwriter.py @@ -22,7 +22,6 @@ from __future__ import with_statement import os -from ctypes.util import find_library from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum, Function, Interface, List, Map, Member, Struct, Union, @@ -80,16 +79,9 @@ and/or use gtk-doc annotations. ''') self.write_tag('c:include', attrs) def _write_namespace(self, namespace, shlibs, cprefix): - libraries = [] - for l in shlibs: - found_libname = find_library(l) - if not found_libname: - found_libname = l - libraries.append(os.path.basename(found_libname)) - attrs = [('name', namespace.name), ('version', namespace.version), - ('shared-library', ','.join(libraries)), + ('shared-library', ','.join(shlibs)), ('c:prefix', cprefix)] with self.tagcontext('namespace', attrs): # We define a custom sorting function here because diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py index e2801593..4b742538 100644 --- a/giscanner/scannermain.py +++ b/giscanner/scannermain.py @@ -32,6 +32,7 @@ from giscanner.dumper import compile_introspection_binary from giscanner.glibtransformer import GLibTransformer, IntrospectionBinary from giscanner.minixpath import myxpath, xpath_assert from giscanner.sourcescanner import SourceScanner +from giscanner.shlibs import resolve_shlibs from giscanner.transformer import Transformer def _get_option_parser(): @@ -319,6 +320,8 @@ def scanner_main(args): binary = compile_introspection_binary(options, glibtransformer.get_get_type_functions()) + shlibs = resolve_shlibs(options, binary, libraries) + glibtransformer.set_introspection_binary(binary) namespace = glibtransformer.parse() @@ -330,7 +333,7 @@ def scanner_main(args): raise SystemExit("ERROR in annotation: %s" % (str(e), )) # Write out AST - writer = Writer(namespace, libraries, transformer.get_includes(), + writer = Writer(namespace, shlibs, transformer.get_includes(), options.packages, options.c_includes, transformer.get_strip_prefix()) data = writer.get_xml() diff --git a/giscanner/shlibs.py b/giscanner/shlibs.py new file mode 100644 index 00000000..38a89e20 --- /dev/null +++ b/giscanner/shlibs.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- Mode: Python -*- +# GObject-Introspection - a framework for introspecting GObject libraries +# Copyright (C) 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + +import re +import subprocess + +from .utils import get_libtool_command + +# Assume ldd output is something vaguely like +# +# libpangoft2-1.0.so.0 => /usr/lib/libpangoft2-1.0.so.0 (0x006c1000) +# +# We say that if something in the output looks like libpangoft2<blah> +# then the *first* such in the output is the soname. We require <blah> +# to start with [^A-Za-z0-9_-] to avoid problems with libpango vs libpangoft2 +# +# The negative lookbehind at the start is to avoid problems if someone +# is crazy enough to name a library liblib<foo> when lib<foo> exists. +# +def _library_pattern(library_name): + return re.compile("(?<![A-Za-z0-9_-])(lib*%s[^A-Za-z0-9_-][^\s\(\)]*)" + % re.escape(library_name)) + +# We want to resolve a set of library names (the <foo> of -l<foo>) +# against a library to find the shared library name. The shared +# library name is suppose to be what you pass to dlopen() (or +# equivalent). And we want to do this using the libraries that 'binary' +# is linking against. The implementation below assumes that we are on an +# ELF-like system where ldd exists and the soname extracted with ldd is +# a filename that can be opened with dlopen(). Alternate implementations +# could be added here. +# +def resolve_shlibs(options, binary, libraries): + args = [] + libtool = get_libtool_command(options) + if libtool: + args.extend(libtool) + args.append('--mode=execute') + args.extend(['ldd', binary.args[0]]) + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + patterns = {} + for library in libraries: + patterns[library] = _library_pattern(library) + + shlibs = [] + for line in proc.stdout: + for library, pattern in patterns.iteritems(): + m = pattern.search(line) + if m: + del patterns[library] + shlibs.append(m.group(1)) + break + + if len(patterns) > 0: + raise SystemExit( + "ERROR: can't resolve libraries to shared libraries: " + + ", ".join(patterns.keys())) + + return shlibs diff --git a/giscanner/utils.py b/giscanner/utils.py index d2752e7b..3a26a1ee 100644 --- a/giscanner/utils.py +++ b/giscanner/utils.py @@ -57,3 +57,25 @@ def extract_libtool(libname): # and pre-2.2. Johan 2008-10-21 libname = libname.replace('.libs/.libs', '.libs') return libname + +# Returns arguments for invoking libtool, if applicable, otherwise None +def get_libtool_command(options): + libtool_infection = not options.nolibtool + if not libtool_infection: + return None + + libtool_path = options.libtool_path + if libtool_path: + # Automake by default sets: + # LIBTOOL = $(SHELL) $(top_builddir)/libtool + # To be strictly correct we would have to parse shell. For now + # we simply split(). + return libtool_path.split(' ') + + try: + subprocess.check_call(['libtool', '--version']) + except subprocess.CalledProcessError, e: + # If libtool's not installed, assume we don't need it + return None + + return ['libtool'] |