1--- a/giscanner/scannermain.py
2+++ b/giscanner/scannermain.py
3@@ -101,6 +101,39 @@
4 return group
5
6
7+def _get_default_fallback_libpath():
8+ # Newer multiple-output-optimized stdenv has an environment variable
9+ # $outputLib which in turn specifies another variable which then is used as
10+ # the destination for the library contents (${!outputLib}/lib).
11+ store_path = os.environ.get(os.environ.get("outputLib")) if "outputLib" in os.environ else None
12+ if store_path is None:
13+ outputs = os.environ.get("outputs", "out").split()
14+ if "lib" in outputs:
15+ # For multiple output derivations let's try whether there is a $lib
16+ # environment variable and use that as the base store path.
17+ store_path = os.environ.get("lib")
18+ elif "out" in outputs:
19+ # Otherwise we have a single output derivation, so the libraries
20+ # most certainly will end up in "$out/lib".
21+ store_path = os.environ.get("out")
22+
23+ if store_path is not None:
24+ # Even if we have a $lib as output, there still should be a $lib/lib
25+ # directory.
26+ return os.path.join(store_path, 'lib')
27+ else:
28+ # If we haven't found a possible scenario, let's return an empty string
29+ # so that the shared library won't be prepended with a path.
30+ #
31+ # Note that this doesn't mean that all hope is lost, because after all
32+ # we can still use --fallback-library-path to set one.
33+ #
34+ # Also, we're not returning None, because that would make it very
35+ # difficult to disable adding fallback paths altogether using something
36+ # like: --fallback-library-path=""
37+ return ""
38+
39+
40 def _get_option_parser():
41 parser = optparse.OptionParser('%prog [options] sources',
42 version='%prog ' + giscanner.__version__)
43@@ -211,6 +244,10 @@
44 parser.add_option("", "--filelist",
45 action="store", dest="filelist", default=[],
46 help="file containing headers and sources to be scanned")
47+ parser.add_option("", "--fallback-library-path",
48+ action="store", dest="fallback_libpath",
49+ default=_get_default_fallback_libpath(),
50+ help="Path to prepend to unknown shared libraries")
51
52 group = get_preprocessor_option_group(parser)
53 parser.add_option_group(group)
54--- a/giscanner/shlibs.py
55+++ b/giscanner/shlibs.py
56@@ -62,6 +62,12 @@
57 $""" % re.escape(library_name), re.VERBOSE)
58
59
60+def _ldd_library_nix_pattern(library_name):
61+ nix_store_dir = re.escape('@nixStoreDir@'.rstrip('/'))
62+ pattern = r'(%s(?:/[^/]*)+lib%s[^A-Za-z0-9_-][^\s\(\)]*)'
63+ return re.compile(pattern % (nix_store_dir, re.escape(library_name)))
64+
65+
66 # This is a what we do for non-la files. We assume that we are on an
67 # ELF-like system where ldd exists and the soname extracted with ldd is
68 # a filename that can be opened with dlopen().
69@@ -110,17 +116,16 @@ def _resolve_non_libtool(options, binary, libraries):
70 if isinstance(output, bytes):
71 output = output.decode("utf-8", "replace")
72
73- # Use absolute paths on OS X to conform to how libraries are usually
74- # referenced on OS X systems, and file names everywhere else.
75- basename = platform.system() != 'Darwin'
76- return resolve_from_ldd_output(libraries, output, basename=basename)
77+ # Never strip away absolute paths in Nix
78+ basename = False
79+ return resolve_from_ldd_output(libraries, output, basename=basename, fallback_libpath=options.fallback_libpath)
80
81
82-def resolve_from_ldd_output(libraries, output, basename=False):
83+def resolve_from_ldd_output(libraries, output, basename=False, fallback_libpath=""):
84 patterns = {}
85 for library in libraries:
86 if not os.path.isfile(library):
87- patterns[library] = _ldd_library_pattern(library)
88+ patterns[library] = (_ldd_library_pattern(library), _ldd_library_nix_pattern(library))
89 if len(patterns) == 0:
90 return []
91
92@@ -129,11 +134,14 @@ def resolve_from_ldd_output(libraries, output, basename=False):
93 if line.endswith(':'):
94 continue
95 for word in line.split():
96- for library, pattern in patterns.items():
97- m = pattern.match(word)
98+ for library, (pattern, nix_pattern) in patterns.items():
99+ if line.find('@nixStoreDir@') != -1:
100+ m = nix_pattern.match(word)
101+ else:
102+ m = pattern.match(word)
103 if m:
104 del patterns[library]
105- shlibs.append(_sanitize_install_name(m.group()))
106+ shlibs.append(os.path.join(fallback_libpath, _sanitize_install_name(m.group())))
107 break
108
109 if len(patterns) > 0:
110--- a/giscanner/utils.py
111+++ b/giscanner/utils.py
112@@ -116,17 +116,11 @@
113 if dlname is None:
114 return None
115
116- # Darwin uses absolute paths where possible; since the libtool files never
117- # contain absolute paths, use the libdir field
118- if platform.system() == 'Darwin':
119- dlbasename = os.path.basename(dlname)
120- libdir = _extract_libdir_field(la_file)
121- if libdir is None:
122- return dlbasename
123- return libdir + '/' + dlbasename
124- # From the comments in extract_libtool(), older libtools had
125- # a path rather than the raw dlname
126- return os.path.basename(dlname)
127+ dlbasename = os.path.basename(dlname)
128+ libdir = _extract_libdir_field(la_file)
129+ if libdir is None:
130+ return dlbasename
131+ return libdir + '/' + dlbasename
132
133
134 def extract_libtool(la_file):