symbolextractor: Add a Windows implementation

Supports both MSVC and MinGW toolchains. Checks for MSVC first, then
falls back to MinGW.
pull/6637/head
Nirbheek Chauhan 4 years ago
parent 3320e13d91
commit cace70c64e
  1. 87
      mesonbuild/scripts/symbolextractor.py
  2. 2
      run_unittests.py

@ -66,24 +66,34 @@ def print_tool_warning(tool: list, msg: str, stderr: str = None):
with open(TOOL_WARNING_FILE, 'w'):
pass
def call_tool(name: str, args: T.List[str]) -> str:
def get_tool(name: str) -> T.List[str]:
evar = name.upper()
if evar in os.environ:
import shlex
name = shlex.split(os.environ[evar])
else:
name = [name]
# Run it
return shlex.split(os.environ[evar])
return [name]
def call_tool(name: str, args: T.List[str], **kwargs) -> str:
tool = get_tool(name)
try:
p, output, e = Popen_safe(name + args)
p, output, e = Popen_safe(tool + args, **kwargs)
except FileNotFoundError:
print_tool_warning(tool, 'not found')
return None
if p.returncode != 0:
print_tool_warning(name, 'does not work', e)
print_tool_warning(tool, 'does not work', e)
return None
return output
def call_tool_nowarn(tool: T.List[str], **kwargs) -> T.Tuple[str, str]:
try:
p, output, e = Popen_safe(tool, **kwargs)
except FileNotFoundError:
return None, '{!r} not found\n'.format(tool[0])
if p.returncode != 0:
return None, e
return output, None
def linux_syms(libfilename: str, outfilename: str):
# Get the name of the library
output = call_tool('readelf', ['-d', libfilename])
@ -129,6 +139,62 @@ def osx_syms(libfilename: str, outfilename: str):
result += [' '.join(x.split()[0:2]) for x in output.split('\n')]
write_if_changed('\n'.join(result) + '\n', outfilename)
def _get_implib_dllname(impfilename: str) -> T.Tuple[T.List[str], str]:
# First try lib.exe, which is provided by MSVC.
# Do not allow overriding by setting the LIB env var, because that is
# already used for something else: it's the list of library paths MSVC
# will search for import libraries while linking.
output, e1 = call_tool_nowarn(['lib', '-list', impfilename])
if output:
# The output is a list of DLLs that each symbol exported by the import
# library is available in. We only build import libraries that point to
# a single DLL, so we can pick any of these. Pick the last one for
# simplicity. Also skip the last line, which is empty.
return output.split('\n')[-2:-1], None
# Next, try dlltool.exe which is provided by MinGW
output, e2 = call_tool_nowarn(get_tool('dlltool') + ['-I', impfilename])
if output:
return [output], None
return ([], (e1 + e2))
def _get_implib_exports(impfilename: str) -> T.Tuple[T.List[str], str]:
# Force dumpbin.exe to use en-US so we can parse its output
env = os.environ.copy()
env['VSLANG'] = '1033'
output, e1 = call_tool_nowarn(get_tool('dumpbin') + ['-exports', impfilename], env=env)
if output:
lines = output.split('\n')
start = lines.index('File Type: LIBRARY')
end = lines.index(' Summary')
return lines[start:end], None
# Next, try nm.exe which is provided by MinGW
output, e2 = call_tool_nowarn(get_tool('nm') + ['--extern-only', '--defined-only',
'--format=posix', impfilename])
if output:
result = []
for line in output.split('\n'):
if ' T ' not in line or line.startswith('.text'):
continue
result.append(line.split(maxsplit=1)[0])
return result, None
return ([], (e1 + e2))
def windows_syms(impfilename: str, outfilename: str):
# Get the name of the library
result, e = _get_implib_dllname(impfilename)
if not result:
print_tool_warning('Both lib.exe and dlltool.exe', 'do not work', e)
dummy_syms(outfilename)
return
# Get a list of all symbols exported
symbols, e = _get_implib_exports(impfilename)
if not symbols:
print_tool_warning('Both dumpbin.exe and nm.exe', 'do not work', e)
dummy_syms(outfilename)
return
result += symbols
write_if_changed('\n'.join(result) + '\n', outfilename)
def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host: str):
if cross_host is not None:
# In case of cross builds just always relink. In theory we could
@ -139,6 +205,13 @@ def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host
linux_syms(libfilename, outfilename)
elif mesonlib.is_osx():
osx_syms(libfilename, outfilename)
elif mesonlib.is_windows():
if os.path.isfile(impfilename):
windows_syms(impfilename, outfilename)
else:
# No import library. Not sure how the DLL is being used, so just
# rebuild everything that links to it every time.
dummy_syms(outfilename)
else:
if not os.path.exists(TOOL_WARNING_FILE):
mlog.warning('Symbol extracting has not been implemented for this '

@ -2591,6 +2591,8 @@ class AllPlatformTests(BasePlatformTests):
Test that no-op changes to the build files such as mtime do not cause
a rebuild of anything.
'''
if is_cygwin():
raise unittest.SkipTest('symbolextractor has not been implemented for Cygwin yet')
testdir = os.path.join(self.common_test_dir, '6 linkshared')
self.init(testdir)
self.build()

Loading…
Cancel
Save