1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
| | Names of libraries included in typelib files are opened by dlopen.
Here we add the full path.
This patch was provided by Luca Bruno <lucabru@src.gnome.org>,
for 'gobject-introspection' 1.40.0 in Nix.
It has since been updated to work with newer versions of
gobject-introspection.
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 1d39ab84..e12ed24e 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -95,6 +95,39 @@ def get_windows_option_group(parser):
return group
+def _get_default_fallback_libpath():
+ # Newer multiple-output-optimized stdenv has an environment variable
+ # $outputLib which in turn specifies another variable which then is used as
+ # the destination for the library contents (${!outputLib}/lib).
+ store_path = os.environ.get(os.environ.get("outputLib")) if "outputLib" in os.environ else None
+ if store_path is None:
+ outputs = os.environ.get("outputs", "out").split()
+ if "lib" in outputs:
+ # For multiple output derivations let's try whether there is a $lib
+ # environment variable and use that as the base store path.
+ store_path = os.environ.get("lib")
+ elif "out" in outputs:
+ # Otherwise we have a single output derivation, so the libraries
+ # most certainly will end up in "$out/lib".
+ store_path = os.environ.get("out")
+
+ if store_path is not None:
+ # Even if we have a $lib as output, there still should be a $lib/lib
+ # directory.
+ return os.path.join(store_path, 'lib')
+ else:
+ # If we haven't found a possible scenario, let's return an empty string
+ # so that the shared library won't be prepended with a path.
+ #
+ # Note that this doesn't mean that all hope is lost, because after all
+ # we can still use --fallback-library-path to set one.
+ #
+ # Also, we're not returning None, because that would make it very
+ # difficult to disable adding fallback paths altogether using something
+ # like: --fallback-library-path=""
+ return ""
+
+
def _get_option_parser():
parser = optparse.OptionParser('%prog [options] sources',
version='%prog ' + giscanner.__version__)
@@ -220,6 +253,10 @@ match the namespace prefix.""")
parser.add_option("", "--compiler",
action="store", dest="compiler", default=None,
help="the C compiler to use internally")
+ parser.add_option("", "--fallback-library-path",
+ action="store", dest="fallback_libpath",
+ default=_get_default_fallback_libpath(),
+ help="Path to prepend to unknown shared libraries")
group = get_preprocessor_option_group(parser)
parser.add_option_group(group)
diff --git a/giscanner/shlibs.py b/giscanner/shlibs.py
index 9f8ab5df..8aa37c99 100644
--- a/giscanner/shlibs.py
+++ b/giscanner/shlibs.py
@@ -57,6 +57,14 @@ def _ldd_library_pattern(library_name):
$""" % re.escape(library_name), re.VERBOSE)
+def _ldd_library_guix_pattern(library_name):
+ store_dir = re.escape(
+ os.environ.get("NIX_STORE", default="/gnu/store")
+ )
+ pattern = r'(%s(?:/[^/]*)+lib%s[^A-Za-z0-9_-][^\s\(\)]*)'
+ return re.compile(pattern % (store_dir, re.escape(library_name)))
+
+
# This is a what we do for non-la files. We assume 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().
@@ -108,7 +116,8 @@ def _resolve_non_libtool(options, binary, libraries):
output = output.decode("utf-8", "replace")
shlibs = resolve_from_ldd_output(libraries, output)
- return list(map(sanitize_shlib_path, shlibs))
+ fallback_libpath = options.fallback_libpath or "";
+ return list(map(lambda p: os.path.join(fallback_libpath, p), map(sanitize_shlib_path, shlibs)))
def sanitize_shlib_path(lib):
@@ -117,19 +126,18 @@ def sanitize_shlib_path(lib):
# In case we get relative paths on macOS (like @rpath) then we fall
# back to the basename as well:
# https://gitlab.gnome.org/GNOME/gobject-introspection/issues/222
- if sys.platform == "darwin":
- if not os.path.isabs(lib):
- return os.path.basename(lib)
- return lib
- else:
+
+ # Always use absolute paths if available
+ if not os.path.isabs(lib):
return os.path.basename(lib)
+ return lib
def resolve_from_ldd_output(libraries, output):
patterns = {}
for library in libraries:
if not os.path.isfile(library):
- patterns[library] = _ldd_library_pattern(library)
+ patterns[library] = (_ldd_library_pattern(library), _ldd_library_guix_pattern(library))
if len(patterns) == 0:
return []
@@ -141,8 +149,12 @@ def resolve_from_ldd_output(libraries, output):
if line.endswith(':'):
continue
for word in line.split():
- for library, pattern in patterns.items():
- m = pattern.match(word)
+ for library, (pattern, guix_pattern) in patterns.items():
+ store_dir = os.environ.get("NIX_STORE", default="/gnu/store")
+ if line.find(store_dir) != -1:
+ m = guix_pattern.match(word)
+ else:
+ m = pattern.match(word)
if m:
del patterns[library]
shlibs.append(m.group())
diff --git a/giscanner/utils.py b/giscanner/utils.py
index 31c7ea48..630002a8 100644
--- a/giscanner/utils.py
+++ b/giscanner/utils.py
@@ -114,16 +114,11 @@ def extract_libtool_shlib(la_file):
if dlname is None:
return None
- # Darwin uses absolute paths where possible; since the libtool files never
- # contain absolute paths, use the libdir field
- if platform.system() == 'Darwin':
- dlbasename = os.path.basename(dlname)
- libdir = _extract_libdir_field(la_file)
- if libdir is None:
- return dlbasename
- return libdir + '/' + dlbasename
- # Older libtools had a path rather than the raw dlname
- return os.path.basename(dlname)
+ dlbasename = os.path.basename(dlname)
+ libdir = _extract_libdir_field(la_file)
+ if libdir is None:
+ return dlbasename
+ return libdir + '/' + dlbasename
# Returns arguments for invoking libtool, if applicable, otherwise None
diff --git a/tests/scanner/test_shlibs.py b/tests/scanner/test_shlibs.py
index a8337c60..7f123103 100644
--- a/tests/scanner/test_shlibs.py
+++ b/tests/scanner/test_shlibs.py
@@ -40,7 +40,8 @@ class TestLddParser(unittest.TestCase):
self.assertEqual(
sanitize_shlib_path('/foo/bar'),
- '/foo/bar' if sys.platform == 'darwin' else 'bar')
+ # Always use an absolute filename for Guix
+ '/foo/bar')
def test_unresolved_library(self):
output = ''
|