blob: 10a9ffda24a2b72490d1fd50b9f8d498e3326c83 [file] [log] [blame]
Steve Dowerfd3664b2015-05-23 09:02:50 -07001"""distutils._msvccompiler
2
3Contains MSVCCompiler, an implementation of the abstract CCompiler class
4for Microsoft Visual Studio 2015.
5
6The module is compatible with VS 2015 and later. You can find legacy support
7for older versions in distutils.msvc9compiler and distutils.msvccompiler.
8"""
9
10# Written by Perry Stoll
11# hacked by Robin Becker and Thomas Heller to do a better job of
12# finding DevStudio (through the registry)
13# ported to VS 2005 and VS 2008 by Christian Heimes
14# ported to VS 2015 by Steve Dower
15
16import os
Steve Dowerfcbe1df2015-09-08 21:39:01 -070017import shutil
18import stat
Steve Dowerfd3664b2015-05-23 09:02:50 -070019import subprocess
Steve Dowerfd3664b2015-05-23 09:02:50 -070020
21from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
22 CompileError, LibError, LinkError
23from distutils.ccompiler import CCompiler, gen_lib_options
24from distutils import log
25from distutils.util import get_platform
26
27import winreg
28from itertools import count
29
Steve Dowerfcbe1df2015-09-08 21:39:01 -070030def _find_vcvarsall(plat_spec):
Steve Dowerf0ccf022015-10-05 10:35:00 -070031 try:
32 key = winreg.OpenKeyEx(
33 winreg.HKEY_LOCAL_MACHINE,
34 r"Software\Microsoft\VisualStudio\SxS\VC7",
35 access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
36 )
37 except OSError:
38 log.debug("Visual C++ is not registered")
39 return None, None
Steve Dowerfd3664b2015-05-23 09:02:50 -070040
Steve Dowerf0ccf022015-10-05 10:35:00 -070041 with key:
Steve Dowerfd3664b2015-05-23 09:02:50 -070042 best_version = 0
43 best_dir = None
44 for i in count():
45 try:
46 v, vc_dir, vt = winreg.EnumValue(key, i)
47 except OSError:
48 break
49 if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
50 try:
51 version = int(float(v))
52 except (ValueError, TypeError):
53 continue
54 if version >= 14 and version > best_version:
55 best_version, best_dir = version, vc_dir
56 if not best_version:
57 log.debug("No suitable Visual C++ version found")
Steve Dowerfcbe1df2015-09-08 21:39:01 -070058 return None, None
Steve Dowerfd3664b2015-05-23 09:02:50 -070059
60 vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
61 if not os.path.isfile(vcvarsall):
62 log.debug("%s cannot be found", vcvarsall)
Steve Dowerfcbe1df2015-09-08 21:39:01 -070063 return None, None
Steve Dowerfd3664b2015-05-23 09:02:50 -070064
Steve Dowerfcbe1df2015-09-08 21:39:01 -070065 vcruntime = None
66 vcruntime_spec = _VCVARS_PLAT_TO_VCRUNTIME_REDIST.get(plat_spec)
67 if vcruntime_spec:
68 vcruntime = os.path.join(best_dir,
69 vcruntime_spec.format(best_version))
70 if not os.path.isfile(vcruntime):
71 log.debug("%s cannot be found", vcruntime)
72 vcruntime = None
73
74 return vcvarsall, vcruntime
Steve Dowerfd3664b2015-05-23 09:02:50 -070075
76def _get_vc_env(plat_spec):
77 if os.getenv("DISTUTILS_USE_SDK"):
78 return {
79 key.lower(): value
80 for key, value in os.environ.items()
81 }
82
Steve Dowerfcbe1df2015-09-08 21:39:01 -070083 vcvarsall, vcruntime = _find_vcvarsall(plat_spec)
Steve Dowerfd3664b2015-05-23 09:02:50 -070084 if not vcvarsall:
85 raise DistutilsPlatformError("Unable to find vcvarsall.bat")
86
87 try:
88 out = subprocess.check_output(
89 '"{}" {} && set'.format(vcvarsall, plat_spec),
90 shell=True,
91 stderr=subprocess.STDOUT,
92 universal_newlines=True,
93 )
94 except subprocess.CalledProcessError as exc:
95 log.error(exc.output)
96 raise DistutilsPlatformError("Error executing {}"
97 .format(exc.cmd))
98
Steve Dowerfcbe1df2015-09-08 21:39:01 -070099 env = {
Steve Dowerfd3664b2015-05-23 09:02:50 -0700100 key.lower(): value
101 for key, _, value in
102 (line.partition('=') for line in out.splitlines())
103 if key and value
104 }
Larry Hastings52e40cd2015-09-09 06:54:57 -0700105
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700106 if vcruntime:
107 env['py_vcruntime_redist'] = vcruntime
108 return env
Steve Dowerfd3664b2015-05-23 09:02:50 -0700109
110def _find_exe(exe, paths=None):
111 """Return path to an MSVC executable program.
112
113 Tries to find the program in several places: first, one of the
114 MSVC program search paths from the registry; next, the directories
115 in the PATH environment variable. If any of those work, return an
116 absolute path that is known to exist. If none of them work, just
117 return the original program name, 'exe'.
118 """
119 if not paths:
120 paths = os.getenv('path').split(os.pathsep)
121 for p in paths:
122 fn = os.path.join(os.path.abspath(p), exe)
123 if os.path.isfile(fn):
124 return fn
125 return exe
126
127# A map keyed by get_platform() return values to values accepted by
128# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
129# the param to cross-compile on x86 targetting amd64.)
130PLAT_TO_VCVARS = {
131 'win32' : 'x86',
132 'win-amd64' : 'amd64',
133}
134
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700135# A map keyed by get_platform() return values to the file under
136# the VC install directory containing the vcruntime redistributable.
137_VCVARS_PLAT_TO_VCRUNTIME_REDIST = {
138 'x86' : 'redist\\x86\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
139 'amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
140 'x86_amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll',
141}
142
143# A set containing the DLLs that are guaranteed to be available for
144# all micro versions of this Python version. Known extension
145# dependencies that are not in this set will be copied to the output
146# path.
147_BUNDLED_DLLS = frozenset(['vcruntime140.dll'])
148
Steve Dowerfd3664b2015-05-23 09:02:50 -0700149class MSVCCompiler(CCompiler) :
150 """Concrete class that implements an interface to Microsoft Visual C++,
151 as defined by the CCompiler abstract class."""
152
153 compiler_type = 'msvc'
154
155 # Just set this so CCompiler's constructor doesn't barf. We currently
156 # don't use the 'set_executables()' bureaucracy provided by CCompiler,
157 # as it really isn't necessary for this sort of single-compiler class.
158 # Would be nice to have a consistent interface with UnixCCompiler,
159 # though, so it's worth thinking about.
160 executables = {}
161
162 # Private class data (need to distinguish C from C++ source for compiler)
163 _c_extensions = ['.c']
164 _cpp_extensions = ['.cc', '.cpp', '.cxx']
165 _rc_extensions = ['.rc']
166 _mc_extensions = ['.mc']
167
168 # Needed for the filename generation methods provided by the
169 # base class, CCompiler.
170 src_extensions = (_c_extensions + _cpp_extensions +
171 _rc_extensions + _mc_extensions)
172 res_extension = '.res'
173 obj_extension = '.obj'
174 static_lib_extension = '.lib'
175 shared_lib_extension = '.dll'
176 static_lib_format = shared_lib_format = '%s%s'
177 exe_extension = '.exe'
178
179
180 def __init__(self, verbose=0, dry_run=0, force=0):
181 CCompiler.__init__ (self, verbose, dry_run, force)
182 # target platform (.plat_name is consistent with 'bdist')
183 self.plat_name = None
184 self.initialized = False
185
186 def initialize(self, plat_name=None):
187 # multi-init means we would need to check platform same each time...
188 assert not self.initialized, "don't init multiple times"
189 if plat_name is None:
190 plat_name = get_platform()
191 # sanity check for platforms to prevent obscure errors later.
192 if plat_name not in PLAT_TO_VCVARS:
193 raise DistutilsPlatformError("--plat-name must be one of {}"
194 .format(tuple(PLAT_TO_VCVARS)))
195
196 # On x86, 'vcvarsall.bat amd64' creates an env that doesn't work;
197 # to cross compile, you use 'x86_amd64'.
198 # On AMD64, 'vcvarsall.bat amd64' is a native build env; to cross
199 # compile use 'x86' (ie, it runs the x86 compiler directly)
200 if plat_name == get_platform() or plat_name == 'win32':
201 # native build or cross-compile to win32
202 plat_spec = PLAT_TO_VCVARS[plat_name]
203 else:
204 # cross compile from win32 -> some 64bit
205 plat_spec = '{}_{}'.format(
206 PLAT_TO_VCVARS[get_platform()],
207 PLAT_TO_VCVARS[plat_name]
208 )
209
210 vc_env = _get_vc_env(plat_spec)
211 if not vc_env:
212 raise DistutilsPlatformError("Unable to find a compatible "
213 "Visual Studio installation.")
214
Steve Dower31202ea2015-08-05 11:39:19 -0700215 self._paths = vc_env.get('path', '')
216 paths = self._paths.split(os.pathsep)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700217 self.cc = _find_exe("cl.exe", paths)
218 self.linker = _find_exe("link.exe", paths)
219 self.lib = _find_exe("lib.exe", paths)
220 self.rc = _find_exe("rc.exe", paths) # resource compiler
221 self.mc = _find_exe("mc.exe", paths) # message compiler
222 self.mt = _find_exe("mt.exe", paths) # message compiler
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700223 self._vcruntime_redist = vc_env.get('py_vcruntime_redist', '')
Steve Dowerfd3664b2015-05-23 09:02:50 -0700224
225 for dir in vc_env.get('include', '').split(os.pathsep):
226 if dir:
227 self.add_include_dir(dir)
228
229 for dir in vc_env.get('lib', '').split(os.pathsep):
230 if dir:
231 self.add_library_dir(dir)
232
233 self.preprocess_options = None
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700234 # If vcruntime_redist is available, link against it dynamically. Otherwise,
235 # use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib
Steve Dower31202ea2015-08-05 11:39:19 -0700236 # later to dynamically link to ucrtbase but not vcruntime.
Steve Dowerfd3664b2015-05-23 09:02:50 -0700237 self.compile_options = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700238 '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700239 ]
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700240 self.compile_options.append('/MD' if self._vcruntime_redist else '/MT')
Larry Hastings52e40cd2015-09-09 06:54:57 -0700241
Steve Dowerfd3664b2015-05-23 09:02:50 -0700242 self.compile_options_debug = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700243 '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700244 ]
245
Steve Dower31202ea2015-08-05 11:39:19 -0700246 ldflags = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700247 '/nologo', '/INCREMENTAL:NO', '/LTCG'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700248 ]
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700249 if not self._vcruntime_redist:
250 ldflags.extend(('/nodefaultlib:libucrt.lib', 'ucrt.lib'))
251
Steve Dower31202ea2015-08-05 11:39:19 -0700252 ldflags_debug = [
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700253 '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'
Steve Dowerfd3664b2015-05-23 09:02:50 -0700254 ]
Steve Dower31202ea2015-08-05 11:39:19 -0700255
256 self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
257 self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
258 self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
259 self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
260 self.ldflags_static = [*ldflags]
261 self.ldflags_static_debug = [*ldflags_debug]
262
263 self._ldflags = {
264 (CCompiler.EXECUTABLE, None): self.ldflags_exe,
265 (CCompiler.EXECUTABLE, False): self.ldflags_exe,
266 (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
267 (CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
268 (CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
269 (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
270 (CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
271 (CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
272 (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
273 }
Steve Dowerfd3664b2015-05-23 09:02:50 -0700274
275 self.initialized = True
276
277 # -- Worker methods ------------------------------------------------
278
279 def object_filenames(self,
280 source_filenames,
281 strip_dir=0,
282 output_dir=''):
Steve Dower31202ea2015-08-05 11:39:19 -0700283 ext_map = {
284 **{ext: self.obj_extension for ext in self.src_extensions},
285 **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions},
286 }
287
288 output_dir = output_dir or ''
Steve Dowerfd3664b2015-05-23 09:02:50 -0700289
290 def make_out_path(p):
291 base, ext = os.path.splitext(p)
292 if strip_dir:
293 base = os.path.basename(base)
294 else:
295 _, base = os.path.splitdrive(base)
296 if base.startswith((os.path.sep, os.path.altsep)):
297 base = base[1:]
298 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700299 # XXX: This may produce absurdly long paths. We should check
300 # the length of the result and trim base until we fit within
301 # 260 characters.
302 return os.path.join(output_dir, base + ext_map[ext])
Steve Dowerfd3664b2015-05-23 09:02:50 -0700303 except LookupError:
304 # Better to raise an exception instead of silently continuing
305 # and later complain about sources and targets having
306 # different lengths
307 raise CompileError("Don't know how to compile {}".format(p))
308
Steve Dower31202ea2015-08-05 11:39:19 -0700309 return list(map(make_out_path, source_filenames))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700310
311
312 def compile(self, sources,
313 output_dir=None, macros=None, include_dirs=None, debug=0,
314 extra_preargs=None, extra_postargs=None, depends=None):
315
316 if not self.initialized:
317 self.initialize()
318 compile_info = self._setup_compile(output_dir, macros, include_dirs,
319 sources, depends, extra_postargs)
320 macros, objects, extra_postargs, pp_opts, build = compile_info
321
322 compile_opts = extra_preargs or []
323 compile_opts.append('/c')
324 if debug:
325 compile_opts.extend(self.compile_options_debug)
326 else:
327 compile_opts.extend(self.compile_options)
328
329
330 add_cpp_opts = False
331
332 for obj in objects:
333 try:
334 src, ext = build[obj]
335 except KeyError:
336 continue
337 if debug:
338 # pass the full pathname to MSVC in debug mode,
339 # this allows the debugger to find the source file
340 # without asking the user to browse for it
341 src = os.path.abspath(src)
342
343 if ext in self._c_extensions:
344 input_opt = "/Tc" + src
345 elif ext in self._cpp_extensions:
346 input_opt = "/Tp" + src
347 add_cpp_opts = True
348 elif ext in self._rc_extensions:
349 # compile .RC to .RES file
350 input_opt = src
351 output_opt = "/fo" + obj
352 try:
353 self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
354 except DistutilsExecError as msg:
355 raise CompileError(msg)
356 continue
357 elif ext in self._mc_extensions:
358 # Compile .MC to .RC file to .RES file.
359 # * '-h dir' specifies the directory for the
360 # generated include file
361 # * '-r dir' specifies the target directory of the
362 # generated RC file and the binary message resource
363 # it includes
364 #
365 # For now (since there are no options to change this),
366 # we use the source-directory for the include file and
367 # the build directory for the RC file and message
368 # resources. This works at least for win32all.
369 h_dir = os.path.dirname(src)
370 rc_dir = os.path.dirname(obj)
371 try:
372 # first compile .MC to .RC and .H file
373 self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
374 base, _ = os.path.splitext(os.path.basename (src))
375 rc_file = os.path.join(rc_dir, base + '.rc')
376 # then compile .RC to .RES file
377 self.spawn([self.rc, "/fo" + obj, rc_file])
378
379 except DistutilsExecError as msg:
380 raise CompileError(msg)
381 continue
382 else:
383 # how to handle this file?
384 raise CompileError("Don't know how to compile {} to {}"
385 .format(src, obj))
386
387 args = [self.cc] + compile_opts + pp_opts
388 if add_cpp_opts:
389 args.append('/EHsc')
390 args.append(input_opt)
391 args.append("/Fo" + obj)
392 args.extend(extra_postargs)
393
394 try:
395 self.spawn(args)
396 except DistutilsExecError as msg:
397 raise CompileError(msg)
398
399 return objects
400
401
402 def create_static_lib(self,
403 objects,
404 output_libname,
405 output_dir=None,
406 debug=0,
407 target_lang=None):
408
409 if not self.initialized:
410 self.initialize()
411 objects, output_dir = self._fix_object_args(objects, output_dir)
412 output_filename = self.library_filename(output_libname,
413 output_dir=output_dir)
414
415 if self._need_link(objects, output_filename):
416 lib_args = objects + ['/OUT:' + output_filename]
417 if debug:
418 pass # XXX what goes here?
419 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700420 log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700421 self.spawn([self.lib] + lib_args)
422 except DistutilsExecError as msg:
423 raise LibError(msg)
424 else:
425 log.debug("skipping %s (up-to-date)", output_filename)
426
427
428 def link(self,
429 target_desc,
430 objects,
431 output_filename,
432 output_dir=None,
433 libraries=None,
434 library_dirs=None,
435 runtime_library_dirs=None,
436 export_symbols=None,
437 debug=0,
438 extra_preargs=None,
439 extra_postargs=None,
440 build_temp=None,
441 target_lang=None):
442
443 if not self.initialized:
444 self.initialize()
445 objects, output_dir = self._fix_object_args(objects, output_dir)
446 fixed_args = self._fix_lib_args(libraries, library_dirs,
447 runtime_library_dirs)
448 libraries, library_dirs, runtime_library_dirs = fixed_args
449
450 if runtime_library_dirs:
451 self.warn("I don't know what to do with 'runtime_library_dirs': "
452 + str(runtime_library_dirs))
453
454 lib_opts = gen_lib_options(self,
455 library_dirs, runtime_library_dirs,
456 libraries)
457 if output_dir is not None:
458 output_filename = os.path.join(output_dir, output_filename)
459
460 if self._need_link(objects, output_filename):
Steve Dower31202ea2015-08-05 11:39:19 -0700461 ldflags = self._ldflags[target_desc, debug]
Steve Dowerfd3664b2015-05-23 09:02:50 -0700462
Steve Dower31202ea2015-08-05 11:39:19 -0700463 export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
Steve Dowerfd3664b2015-05-23 09:02:50 -0700464
465 ld_args = (ldflags + lib_opts + export_opts +
466 objects + ['/OUT:' + output_filename])
467
468 # The MSVC linker generates .lib and .exp files, which cannot be
469 # suppressed by any linker switches. The .lib files may even be
470 # needed! Make sure they are generated in the temporary build
471 # directory. Since they have different names for debug and release
472 # builds, they can go into the same directory.
473 build_temp = os.path.dirname(objects[0])
474 if export_symbols is not None:
475 (dll_name, dll_ext) = os.path.splitext(
476 os.path.basename(output_filename))
477 implib_file = os.path.join(
478 build_temp,
479 self.library_filename(dll_name))
480 ld_args.append ('/IMPLIB:' + implib_file)
481
Steve Dowerfd3664b2015-05-23 09:02:50 -0700482 if extra_preargs:
483 ld_args[:0] = extra_preargs
484 if extra_postargs:
485 ld_args.extend(extra_postargs)
486
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700487 output_dir = os.path.dirname(os.path.abspath(output_filename))
488 self.mkpath(output_dir)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700489 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700490 log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
Steve Dowerfd3664b2015-05-23 09:02:50 -0700491 self.spawn([self.linker] + ld_args)
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700492 self._copy_vcruntime(output_dir)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700493 except DistutilsExecError as msg:
494 raise LinkError(msg)
Steve Dowerfd3664b2015-05-23 09:02:50 -0700495 else:
496 log.debug("skipping %s (up-to-date)", output_filename)
497
Steve Dowerfcbe1df2015-09-08 21:39:01 -0700498 def _copy_vcruntime(self, output_dir):
499 vcruntime = self._vcruntime_redist
500 if not vcruntime or not os.path.isfile(vcruntime):
501 return
502
503 if os.path.basename(vcruntime).lower() in _BUNDLED_DLLS:
504 return
505
506 log.debug('Copying "%s"', vcruntime)
507 vcruntime = shutil.copy(vcruntime, output_dir)
508 os.chmod(vcruntime, stat.S_IWRITE)
509
Steve Dower31202ea2015-08-05 11:39:19 -0700510 def spawn(self, cmd):
511 old_path = os.getenv('path')
Steve Dowerfd3664b2015-05-23 09:02:50 -0700512 try:
Steve Dower31202ea2015-08-05 11:39:19 -0700513 os.environ['path'] = self._paths
514 return super().spawn(cmd)
515 finally:
516 os.environ['path'] = old_path
Steve Dowerfd3664b2015-05-23 09:02:50 -0700517
518 # -- Miscellaneous methods -----------------------------------------
519 # These are all used by the 'gen_lib_options() function, in
520 # ccompiler.py.
521
522 def library_dir_option(self, dir):
523 return "/LIBPATH:" + dir
524
525 def runtime_library_dir_option(self, dir):
526 raise DistutilsPlatformError(
527 "don't know how to set runtime library search path for MSVC")
528
529 def library_option(self, lib):
530 return self.library_filename(lib)
531
532 def find_library_file(self, dirs, lib, debug=0):
533 # Prefer a debugging library if found (and requested), but deal
534 # with it if we don't have one.
535 if debug:
536 try_names = [lib + "_d", lib]
537 else:
538 try_names = [lib]
539 for dir in dirs:
540 for name in try_names:
541 libfile = os.path.join(dir, self.library_filename(name))
542 if os.path.isfile(libfile):
543 return libfile
544 else:
545 # Oops, didn't find it in *any* of 'dirs'
546 return None