aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorNirbheek Chauhan <nirbheek@centricular.com>2020-02-17 00:18:36 +0530
committerNirbheek Chauhan <nirbheek@centricular.com>2020-02-22 06:49:34 +0530
commitcace70c64eab4c4f5ba5bca7e76e02cab275b025 (patch)
tree4ad9ad439dd95e876a41c721fa3c171b507a7fb9 /mesonbuild
parent3320e13d91d68f0a4898901c168b3981b76dec41 (diff)
downloadmeson-cace70c64eab4c4f5ba5bca7e76e02cab275b025.zip
meson-cace70c64eab4c4f5ba5bca7e76e02cab275b025.tar.gz
meson-cace70c64eab4c4f5ba5bca7e76e02cab275b025.tar.bz2
symbolextractor: Add a Windows implementation
Supports both MSVC and MinGW toolchains. Checks for MSVC first, then falls back to MinGW.
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/scripts/symbolextractor.py87
1 files changed, 80 insertions, 7 deletions
diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py
index fd42dde..2172243 100644
--- a/mesonbuild/scripts/symbolextractor.py
+++ b/mesonbuild/scripts/symbolextractor.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 '