[gobject-introspection] Resolve library names to shared libraries ourselves



commit af6bcb48d777f683384d9b8497e1b0edba5b16e7
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Thu Aug 13 16:09:27 2009 -0400

    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

 giscanner/Makefile.am     |    1 +
 giscanner/dumper.py       |   24 +-------------
 giscanner/girwriter.py    |   10 +-----
 giscanner/scannermain.py  |    5 ++-
 giscanner/shlibs.py       |   77 +++++++++++++++++++++++++++++++++++++++++++++
 giscanner/utils.py        |   22 +++++++++++++
 tests/scanner/Makefile.am |    4 +-
 7 files changed, 109 insertions(+), 34 deletions(-)
---
diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am
index 1ecd977..098235b 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 622b252..b8f16c9 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 ddd562c..aad55ef 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 e280159..4b74253 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 0000000..38a89e2
--- /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 d2752e7..3a26a1e 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']
diff --git a/tests/scanner/Makefile.am b/tests/scanner/Makefile.am
index ed8d396..36aedbf 100644
--- a/tests/scanner/Makefile.am
+++ b/tests/scanner/Makefile.am
@@ -143,10 +143,10 @@ post-check:
 	@true
 
 %.typelib: %.gir $(top_builddir)/tools/g-ir-compiler$(EXEEXT) Makefile
-	LD_LIBRARY_PATH=$(top_builddir)/girepository/.libs$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} $(top_builddir)/tools/g-ir-compiler --includedir=. --includedir=$(top_builddir)/gir $< -o $@
+	LD_LIBRARY_PATH=$(top_builddir)/girepository/.libs:$(builddir)/.libs$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} $(top_builddir)/tools/g-ir-compiler --includedir=. --includedir=$(top_builddir)/gir $< -o $@
 
 %.tgir: %.typelib $(top_builddir)/tools/g-ir-generate$(EXEEXT) Makefile
-	LD_LIBRARY_PATH=$(top_builddir)/girepository/.libs$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} $(top_builddir)/tools/g-ir-generate --includedir=. --includedir=$(top_builddir)/gir $< -o $@
+	LD_LIBRARY_PATH=$(top_builddir)/girepository/.libs:$(builddir)/.libs$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} $(top_builddir)/tools/g-ir-generate --includedir=. --includedir=$(top_builddir)/gir $< -o $@
 
 %.tgir.check: %.tgir
 	@diff -u -U 10 $(srcdir)/$*-expected.tgir $*.tgir; \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]