|
1 | | -import sys, os |
2 | | -import contextlib |
| 1 | +import os |
| 2 | +import shutil |
3 | 3 | import subprocess |
| 4 | +import sys |
4 | 5 |
|
5 | 6 | # find_library(name) returns the pathname of a library, or None. |
6 | 7 | if os.name == "nt": |
@@ -94,102 +95,134 @@ def find_library(name): |
94 | 95 | import re, tempfile |
95 | 96 |
|
96 | 97 | def _findLib_gcc(name): |
97 | | - expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name) |
98 | | - fdout, ccout = tempfile.mkstemp() |
99 | | - os.close(fdout) |
100 | | - cmd = 'if type gcc >/dev/null 2>&1; then CC=gcc; elif type cc >/dev/null 2>&1; then CC=cc;else exit 10; fi;' \ |
101 | | - 'LANG=C LC_ALL=C $CC -Wl,-t -o ' + ccout + ' 2>&1 -l' + name |
| 98 | + # Run GCC's linker with the -t (aka --trace) option and examine the |
| 99 | + # library name it prints out. The GCC command will fail because we |
| 100 | + # haven't supplied a proper program with main(), but that does not |
| 101 | + # matter. |
| 102 | + expr = os.fsencode(r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)) |
| 103 | + |
| 104 | + c_compiler = shutil.which('gcc') |
| 105 | + if not c_compiler: |
| 106 | + c_compiler = shutil.which('cc') |
| 107 | + if not c_compiler: |
| 108 | + # No C compiler available, give up |
| 109 | + return None |
| 110 | + |
| 111 | + temp = tempfile.NamedTemporaryFile() |
102 | 112 | try: |
103 | | - f = os.popen(cmd) |
104 | | - try: |
105 | | - trace = f.read() |
106 | | - finally: |
107 | | - rv = f.close() |
| 113 | + args = [c_compiler, '-Wl,-t', '-o', temp.name, '-l' + name] |
| 114 | + |
| 115 | + env = dict(os.environ) |
| 116 | + env['LC_ALL'] = 'C' |
| 117 | + env['LANG'] = 'C' |
| 118 | + proc = subprocess.Popen(args, |
| 119 | + stdout=subprocess.PIPE, |
| 120 | + stderr=subprocess.STDOUT, |
| 121 | + env=env) |
| 122 | + with proc: |
| 123 | + trace = proc.stdout.read() |
108 | 124 | finally: |
109 | 125 | try: |
110 | | - os.unlink(ccout) |
| 126 | + temp.close() |
111 | 127 | except FileNotFoundError: |
| 128 | + # Raised if the file was already removed, which is the normal |
| 129 | + # behaviour of GCC if linking fails |
112 | 130 | pass |
113 | | - if rv == 10: |
114 | | - raise OSError('gcc or cc command not found') |
115 | 131 | res = re.search(expr, trace) |
116 | 132 | if not res: |
117 | 133 | return None |
118 | | - return res.group(0) |
| 134 | + return os.fsdecode(res.group(0)) |
119 | 135 |
|
120 | 136 |
|
121 | 137 | if sys.platform == "sunos5": |
122 | 138 | # use /usr/ccs/bin/dump on solaris |
123 | 139 | def _get_soname(f): |
124 | 140 | if not f: |
125 | 141 | return None |
126 | | - cmd = "/usr/ccs/bin/dump -Lpv 2>/dev/null " + f |
127 | | - with contextlib.closing(os.popen(cmd)) as f: |
128 | | - data = f.read() |
129 | | - res = re.search(r'\[.*\]\sSONAME\s+([^\s]+)', data) |
| 142 | + |
| 143 | + proc = subprocess.Popen(("/usr/ccs/bin/dump", "-Lpv", f), |
| 144 | + stdout=subprocess.PIPE, |
| 145 | + stderr=subprocess.DEVNULL) |
| 146 | + with proc: |
| 147 | + data = proc.stdout.read() |
| 148 | + res = re.search(br'\[.*\]\sSONAME\s+([^\s]+)', data) |
130 | 149 | if not res: |
131 | 150 | return None |
132 | | - return res.group(1) |
| 151 | + return os.fsdecode(res.group(1)) |
133 | 152 | else: |
134 | 153 | def _get_soname(f): |
135 | 154 | # assuming GNU binutils / ELF |
136 | 155 | if not f: |
137 | 156 | return None |
138 | | - cmd = 'if ! type objdump >/dev/null 2>&1; then exit 10; fi;' \ |
139 | | - "objdump -p -j .dynamic 2>/dev/null " + f |
140 | | - f = os.popen(cmd) |
141 | | - try: |
142 | | - dump = f.read() |
143 | | - finally: |
144 | | - rv = f.close() |
145 | | - if rv == 10: |
146 | | - raise OSError('objdump command not found') |
147 | | - res = re.search(r'\sSONAME\s+([^\s]+)', dump) |
| 157 | + objdump = shutil.which('objdump') |
| 158 | + if not objdump: |
| 159 | + # objdump is not available, give up |
| 160 | + return None |
| 161 | + |
| 162 | + proc = subprocess.Popen((objdump, '-p', '-j', '.dynamic', f), |
| 163 | + stdout=subprocess.PIPE, |
| 164 | + stderr=subprocess.DEVNULL) |
| 165 | + with proc: |
| 166 | + dump = proc.stdout.read() |
| 167 | + res = re.search(br'\sSONAME\s+([^\s]+)', dump) |
148 | 168 | if not res: |
149 | 169 | return None |
150 | | - return res.group(1) |
| 170 | + return os.fsdecode(res.group(1)) |
151 | 171 |
|
152 | 172 | if sys.platform.startswith(("freebsd", "openbsd", "dragonfly")): |
153 | 173 |
|
154 | 174 | def _num_version(libname): |
155 | 175 | # "libxyz.so.MAJOR.MINOR" => [ MAJOR, MINOR ] |
156 | | - parts = libname.split(".") |
| 176 | + parts = libname.split(b".") |
157 | 177 | nums = [] |
158 | 178 | try: |
159 | 179 | while parts: |
160 | 180 | nums.insert(0, int(parts.pop())) |
161 | 181 | except ValueError: |
162 | 182 | pass |
163 | | - return nums or [ sys.maxsize ] |
| 183 | + return nums or [sys.maxsize] |
164 | 184 |
|
165 | 185 | def find_library(name): |
166 | 186 | ename = re.escape(name) |
167 | 187 | expr = r':-l%s\.\S+ => \S*/(lib%s\.\S+)' % (ename, ename) |
168 | | - with contextlib.closing(os.popen('/sbin/ldconfig -r 2>/dev/null')) as f: |
169 | | - data = f.read() |
| 188 | + expr = os.fsencode(expr) |
| 189 | + |
| 190 | + proc = subprocess.Popen(('/sbin/ldconfig', '-r'), |
| 191 | + stdout=subprocess.PIPE, |
| 192 | + stderr=subprocess.DEVNULL) |
| 193 | + with proc: |
| 194 | + data = proc.stdout.read() |
| 195 | + |
170 | 196 | res = re.findall(expr, data) |
171 | 197 | if not res: |
172 | 198 | return _get_soname(_findLib_gcc(name)) |
173 | 199 | res.sort(key=_num_version) |
174 | | - return res[-1] |
| 200 | + return os.fsdecode(res[-1]) |
175 | 201 |
|
176 | 202 | elif sys.platform == "sunos5": |
177 | 203 |
|
178 | 204 | def _findLib_crle(name, is64): |
179 | 205 | if not os.path.exists('/usr/bin/crle'): |
180 | 206 | return None |
181 | 207 |
|
| 208 | + env = dict(os.environ) |
| 209 | + env['LC_ALL'] = 'C' |
| 210 | + |
182 | 211 | if is64: |
183 | | - cmd = 'env LC_ALL=C /usr/bin/crle -64 2>/dev/null' |
| 212 | + args = ('/usr/bin/crle', '-64') |
184 | 213 | else: |
185 | | - cmd = 'env LC_ALL=C /usr/bin/crle 2>/dev/null' |
| 214 | + args = ('/usr/bin/crle',) |
186 | 215 |
|
187 | 216 | paths = None |
188 | | - with contextlib.closing(os.popen(cmd)) as f: |
189 | | - for line in f.readlines(): |
| 217 | + proc = subprocess.Popen(args, |
| 218 | + stdout=subprocess.PIPE, |
| 219 | + stderr=subprocess.DEVNULL, |
| 220 | + env=env) |
| 221 | + with proc: |
| 222 | + for line in proc.stdout: |
190 | 223 | line = line.strip() |
191 | | - if line.startswith('Default Library Path (ELF):'): |
192 | | - paths = line.split()[4] |
| 224 | + if line.startswith(b'Default Library Path (ELF):'): |
| 225 | + paths = os.fsdecode(line).split()[4] |
193 | 226 |
|
194 | 227 | if not paths: |
195 | 228 | return None |
|
0 commit comments