155 строки
4.5 KiB
Python
155 строки
4.5 KiB
Python
#!/usr/bin/env python
|
|
|
|
import errno
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
WIN_TOOL = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
|
'gyp-win-tool-original'))
|
|
# These are namespaces whose symbols aren't needed by embedding apps. We don't
|
|
# export them to keep the number of exports under the linker's limit (65535).
|
|
UNEXPORTED_NAMESPACES = set([
|
|
'cc',
|
|
'gpu',
|
|
'media',
|
|
'ppapi',
|
|
])
|
|
|
|
|
|
def main(args):
|
|
# When invoked directly from ninja, our command line will contain many
|
|
# subcommands joined by &&. If that has happened, lets reinvoke ourselves
|
|
# via those subcommands so we can handle each one individually.
|
|
if '&&' in args:
|
|
return subprocess.call([sys.executable] + args, shell=True)
|
|
|
|
args = args[1:]
|
|
|
|
if 'lib.exe' in args:
|
|
retcode = subprocess.call([sys.executable, WIN_TOOL] + args)
|
|
if retcode == 0:
|
|
save_exports(args)
|
|
return retcode
|
|
|
|
if '@chromiumcontent.dll.rsp' in args:
|
|
add_definition_file(args)
|
|
return subprocess.call([sys.executable, WIN_TOOL] + args)
|
|
|
|
|
|
def save_exports(args):
|
|
out_args = [arg for arg in args if arg.startswith('/OUT:')]
|
|
if len(out_args) != 1:
|
|
return
|
|
lib = out_args[0][len('/OUT:'):]
|
|
save_exports_for_lib(lib)
|
|
|
|
|
|
def save_exports_for_lib(lib):
|
|
exports = exports_from_lib(lib)
|
|
with open(exports_file_for_lib(lib), 'w') as f:
|
|
for export in exports:
|
|
f.write(export)
|
|
f.write(os.linesep)
|
|
|
|
|
|
def exports_from_lib(lib):
|
|
exports = {}
|
|
process = subprocess.Popen(['dumpbin.exe', '/directives', lib],
|
|
env=get_env('environment.x86'),
|
|
shell=True,
|
|
stdout=subprocess.PIPE)
|
|
for line in process.stdout:
|
|
stripped = line.strip()
|
|
if stripped.startswith('/EXPORT:'):
|
|
export = stripped[len('/EXPORT:'):].replace(',', ' ')
|
|
if export[0] == '_':
|
|
export = export[1:]
|
|
exports[export] = True
|
|
process.wait()
|
|
return exports.keys()
|
|
|
|
|
|
def exports_file_for_lib(lib):
|
|
return '{0}.exports'.format(lib)
|
|
|
|
|
|
def add_definition_file(args):
|
|
rsp = [arg for arg in args if arg.endswith('.rsp')][0][1:]
|
|
libs = libs_from_rsp(rsp)
|
|
exports = filter_exports(read_saved_exports_from_libs(libs))
|
|
lines = ['EXPORTS'] + sorted(exports)
|
|
with open('chromiumcontent.def', 'w') as f:
|
|
for line in lines:
|
|
f.write('{0}{1}'.format(line, os.linesep))
|
|
with open(rsp, 'a') as r:
|
|
r.write('{0}/DEF:chromiumcontent.def{0}'.format(os.linesep))
|
|
|
|
|
|
def filter_exports(symbols):
|
|
pattern = re.compile('@(?P<namespace>\w+)@@')
|
|
def should_reexport_symbol(symbol):
|
|
match = pattern.search(symbol)
|
|
if not match:
|
|
return True
|
|
return match.group('namespace') not in UNEXPORTED_NAMESPACES
|
|
return filter(should_reexport_symbol, symbols)
|
|
|
|
|
|
def libs_from_rsp(rsp):
|
|
libs = []
|
|
with open(rsp, 'r') as r:
|
|
for line in r:
|
|
words = line.split()
|
|
libs.extend(w for w in words if w.startswith('obj\\') and
|
|
w.endswith('.lib'))
|
|
return libs
|
|
|
|
|
|
def read_saved_exports_from_libs(libs):
|
|
exports = {}
|
|
for lib in libs:
|
|
for export in read_saved_exports_from_one_lib(lib):
|
|
exports[export] = True
|
|
return exports.keys()
|
|
|
|
|
|
def read_saved_exports_from_one_lib(lib):
|
|
exports = {}
|
|
|
|
# Each .lib gets two chances. If the first chance fails, we'll save the
|
|
# exports and try again. If the second chance fails, we're done.
|
|
for attempt in range(2):
|
|
try:
|
|
with open(exports_file_for_lib(lib)) as f:
|
|
for line in f:
|
|
exports[line.strip()] = True
|
|
break
|
|
except IOError as e:
|
|
if e.errno != errno.ENOENT:
|
|
raise
|
|
if attempt > 0:
|
|
raise
|
|
# There's no .exports file for this .lib. Let's save the
|
|
# exports and try again.
|
|
save_exports_for_lib(lib)
|
|
|
|
return exports.keys()
|
|
|
|
|
|
def get_env(arch):
|
|
"""Gets the saved environment from a file for a given architecture."""
|
|
# The environment is saved as an "environment block" (see CreateProcess
|
|
# and msvs_emulation for details). We convert to a dict here.
|
|
# Drop last 2 NULs, one for list terminator, one for trailing vs.
|
|
# separator.
|
|
pairs = open(arch).read()[:-2].split('\0')
|
|
kvs = [item.split('=', 1) for item in pairs]
|
|
return dict(kvs)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv))
|