178 lines
6.9 KiB
Python
178 lines
6.9 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
"""Script to generate Chromium's Abseil .def files at roll time.
|
||
|
|
||
|
This script generates //third_party/abseil-app/absl/symbols_*.def at Abseil
|
||
|
roll time.
|
||
|
|
||
|
Since Abseil doesn't export symbols, Chromium is forced to consider all
|
||
|
Abseil's symbols as publicly visible. On POSIX it is possible to use
|
||
|
-fvisibility=default but on Windows a .def file with all the symbols
|
||
|
is needed.
|
||
|
|
||
|
Unless you are on a Windows machine, you need to set up your Chromium
|
||
|
checkout for cross-compilation by following the instructions at
|
||
|
https://chromium.googlesource.com/chromium/src.git/+/main/docs/win_cross.md.
|
||
|
If you are on Windows, you may need to tweak this script to run, e.g. by
|
||
|
changing "gn" to "gn.bat", changing "llvm-nm" to the name of your copy of
|
||
|
llvm-nm, etc.
|
||
|
"""
|
||
|
|
||
|
import fnmatch
|
||
|
import logging
|
||
|
import os
|
||
|
import re
|
||
|
import subprocess
|
||
|
import sys
|
||
|
import tempfile
|
||
|
import time
|
||
|
|
||
|
# Matches a mangled symbol that has 'absl' in it, this should be a good
|
||
|
# enough heuristic to select Abseil symbols to list in the .def file.
|
||
|
ABSL_SYM_RE = re.compile(r'0* [BT] (?P<symbol>(\?+)[^\?].*absl.*)')
|
||
|
if sys.platform == 'win32':
|
||
|
# Typical dumpbin /symbol lines look like this:
|
||
|
# 04B 0000000C SECT14 notype Static | ?$S1@?1??SetCurrent
|
||
|
# ThreadIdentity@base_internal@absl@@YAXPAUThreadIdentity@12@P6AXPAX@Z@Z@4IA
|
||
|
# (unsigned int `void __cdecl absl::base_internal::SetCurrentThreadIdentity...
|
||
|
# We need to start on "| ?" and end on the first " (" (stopping on space would
|
||
|
# also work).
|
||
|
# This regex is identical inside the () characters except for the ? after .*,
|
||
|
# which is needed to prevent greedily grabbing the undecorated version of the
|
||
|
# symbols.
|
||
|
ABSL_SYM_RE = '.*External \| (?P<symbol>(\?+)[^\?].*?absl.*?) \(.*'
|
||
|
# Typical exported symbols in dumpbin /directives look like:
|
||
|
# /EXPORT:?kHexChar@numbers_internal@absl@@3QBDB,DATA
|
||
|
ABSL_EXPORTED_RE = '.*/EXPORT:(.*),.*'
|
||
|
|
||
|
|
||
|
def _DebugOrRelease(is_debug):
|
||
|
return 'dbg' if is_debug else 'rel'
|
||
|
|
||
|
|
||
|
def _GenerateDefFile(cpu, is_debug, extra_gn_args=[], suffix=None):
|
||
|
"""Generates a .def file for the absl component build on the specified CPU."""
|
||
|
if extra_gn_args:
|
||
|
assert suffix != None, 'suffix is needed when extra_gn_args is used'
|
||
|
|
||
|
flavor = _DebugOrRelease(is_debug)
|
||
|
gn_args = [
|
||
|
'ffmpeg_branding = "Chrome"',
|
||
|
'is_component_build = true',
|
||
|
'is_debug = {}'.format(str(is_debug).lower()),
|
||
|
'proprietary_codecs = true',
|
||
|
'symbol_level = 0',
|
||
|
'target_cpu = "{}"'.format(cpu),
|
||
|
'target_os = "win"',
|
||
|
]
|
||
|
gn_args.extend(extra_gn_args)
|
||
|
|
||
|
gn = 'gn'
|
||
|
autoninja = 'autoninja'
|
||
|
symbol_dumper = ['third_party/llvm-build/Release+Asserts/bin/llvm-nm']
|
||
|
if sys.platform == 'win32':
|
||
|
gn = 'gn.bat'
|
||
|
autoninja = 'autoninja.bat'
|
||
|
symbol_dumper = ['dumpbin', '/symbols']
|
||
|
import shutil
|
||
|
if not shutil.which('dumpbin'):
|
||
|
logging.error('dumpbin not found. Run tools\win\setenv.bat.')
|
||
|
exit(1)
|
||
|
with tempfile.TemporaryDirectory() as out_dir:
|
||
|
logging.info('[%s - %s] Creating tmp out dir in %s', cpu, flavor, out_dir)
|
||
|
subprocess.check_call([gn, 'gen', out_dir, '--args=' + ' '.join(gn_args)],
|
||
|
cwd=os.getcwd())
|
||
|
logging.info('[%s - %s] gn gen completed', cpu, flavor)
|
||
|
subprocess.check_call(
|
||
|
[autoninja, '-C', out_dir, 'third_party/abseil-cpp:absl_component_deps'],
|
||
|
cwd=os.getcwd())
|
||
|
logging.info('[%s - %s] autoninja completed', cpu, flavor)
|
||
|
|
||
|
obj_files = []
|
||
|
for root, _dirnames, filenames in os.walk(
|
||
|
os.path.join(out_dir, 'obj', 'third_party', 'abseil-cpp')):
|
||
|
matched_files = fnmatch.filter(filenames, '*.obj')
|
||
|
obj_files.extend((os.path.join(root, f) for f in matched_files))
|
||
|
|
||
|
logging.info('[%s - %s] Found %d object files.', cpu, flavor, len(obj_files))
|
||
|
|
||
|
absl_symbols = set()
|
||
|
dll_exports = set()
|
||
|
if sys.platform == 'win32':
|
||
|
for f in obj_files:
|
||
|
# Track all of the functions exported with __declspec(dllexport) and
|
||
|
# don't list them in the .def file - double-exports are not allowed. The
|
||
|
# error is "lld-link: error: duplicate /export option".
|
||
|
exports_out = subprocess.check_output(['dumpbin', '/directives', f], cwd=os.getcwd())
|
||
|
for line in exports_out.splitlines():
|
||
|
line = line.decode('utf-8')
|
||
|
match = re.match(ABSL_EXPORTED_RE, line)
|
||
|
if match:
|
||
|
dll_exports.add(match.groups()[0])
|
||
|
for f in obj_files:
|
||
|
stdout = subprocess.check_output(symbol_dumper + [f], cwd=os.getcwd())
|
||
|
for line in stdout.splitlines():
|
||
|
try:
|
||
|
line = line.decode('utf-8')
|
||
|
except UnicodeDecodeError:
|
||
|
# Due to a dumpbin bug there are sometimes invalid utf-8 characters in
|
||
|
# the output. This only happens on an unimportant line so it can
|
||
|
# safely and silently be skipped.
|
||
|
# https://developercommunity.visualstudio.com/content/problem/1091330/dumpbin-symbols-produces-randomly-wrong-output-on.html
|
||
|
continue
|
||
|
match = re.match(ABSL_SYM_RE, line)
|
||
|
if match:
|
||
|
symbol = match.group('symbol')
|
||
|
assert symbol.count(' ') == 0, ('Regex matched too much, probably got '
|
||
|
'undecorated name as well')
|
||
|
# Avoid getting names exported with dllexport, to avoid
|
||
|
# "lld-link: error: duplicate /export option" on symbols such as:
|
||
|
# ?kHexChar@numbers_internal@absl@@3QBDB
|
||
|
if symbol in dll_exports:
|
||
|
continue
|
||
|
# Avoid to export deleting dtors since they trigger
|
||
|
# "lld-link: error: export of deleting dtor" linker errors, see
|
||
|
# crbug.com/1201277.
|
||
|
if symbol.startswith('??_G'):
|
||
|
continue
|
||
|
absl_symbols.add(symbol)
|
||
|
|
||
|
logging.info('[%s - %s] Found %d absl symbols.', cpu, flavor, len(absl_symbols))
|
||
|
|
||
|
if extra_gn_args:
|
||
|
def_file = os.path.join('third_party', 'abseil-cpp',
|
||
|
'symbols_{}_{}_{}.def'.format(cpu, flavor, suffix))
|
||
|
else:
|
||
|
def_file = os.path.join('third_party', 'abseil-cpp',
|
||
|
'symbols_{}_{}.def'.format(cpu, flavor))
|
||
|
|
||
|
with open(def_file, 'w', newline='') as f:
|
||
|
f.write('EXPORTS\n')
|
||
|
for s in sorted(absl_symbols):
|
||
|
f.write(' {}\n'.format(s))
|
||
|
|
||
|
# Hack, it looks like there is a race in the directory cleanup.
|
||
|
time.sleep(10)
|
||
|
|
||
|
logging.info('[%s - %s] .def file successfully generated.', cpu, flavor)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
logging.getLogger().setLevel(logging.INFO)
|
||
|
|
||
|
if sys.version_info.major == 2:
|
||
|
logging.error('This script requires Python 3.')
|
||
|
exit(1)
|
||
|
|
||
|
if not os.getcwd().endswith('src') or not os.path.exists('chrome/browser'):
|
||
|
logging.error('Run this script from a chromium/src/ directory.')
|
||
|
exit(1)
|
||
|
|
||
|
_GenerateDefFile('x86', True)
|
||
|
_GenerateDefFile('x86', False)
|
||
|
_GenerateDefFile('x64', True)
|
||
|
_GenerateDefFile('x64', False)
|
||
|
_GenerateDefFile('x64', False, ['is_asan = true'], 'asan')
|
||
|
_GenerateDefFile('arm64', True)
|
||
|
_GenerateDefFile('arm64', False)
|