Bug 1235132 - Add support for a more-or-less cross-platform symbols file. r=gps

Currently, one needs to define DEFFILE or LD_VERSION_SCRIPT appropriately,
and somehow deal with the fact that their input format is different, which
currently relies on manual invocations of the convert_def_file script, with
awkward aggregations.

This simplifies the problem by using a simple list of symbols, with
preprocessing, allowing #includes.
This commit is contained in:
Mike Hommey 2015-12-25 17:36:16 +09:00
parent 0ff76be786
commit 84bdf4908c
6 changed files with 135 additions and 1 deletions

View File

@ -462,6 +462,19 @@ EXTRA_DEPS += $(LD_VERSION_SCRIPT)
endif
endif
ifdef SYMBOLS_FILE
ifdef GCC_USE_GNU_LD
EXTRA_DSO_LDOPTS += -Wl,--version-script,$(SYMBOLS_FILE)
else
ifeq ($(OS_TARGET),Darwin)
EXTRA_DSO_LDOPTS += -Wl,-exported_symbols_list,$(SYMBOLS_FILE)
endif
ifeq ($(OS_TARGET),WINNT)
EXTRA_DSO_LDOPTS += -DEF:$(call normalizepath,$(SYMBOLS_FILE))
endif
endif
EXTRA_DEPS += $(SYMBOLS_FILE)
endif
#
# GNU doesn't have path length limitation
#

View File

@ -0,0 +1,79 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import absolute_import, print_function, unicode_literals
import buildconfig
import os
from StringIO import StringIO
from mozbuild.preprocessor import Preprocessor
def generate_symbols_file(output, input):
''' '''
input = os.path.abspath(input)
pp = Preprocessor()
pp.context.update(buildconfig.defines)
# Hack until MOZ_DEBUG_FLAGS are simply part of buildconfig.defines
if buildconfig.substs['MOZ_DEBUG']:
pp.context['DEBUG'] = '1'
# Ensure @DATA@ works as expected (see the Windows section further below)
if buildconfig.substs['OS_TARGET'] == 'WINNT':
pp.context['DATA'] = 'DATA'
else:
pp.context['DATA'] = ''
pp.out = StringIO()
pp.do_filter('substitution')
pp.do_include(input)
symbols = [s.strip() for s in pp.out.getvalue().splitlines() if s.strip()]
if buildconfig.substs['GCC_USE_GNU_LD']:
# A linker version script is generated for GNU LD that looks like the
# following:
# {
# global:
# symbol1;
# symbol2;
# ...
# local:
# *;
# };
output.write('{\nglobal:\n %s;\nlocal:\n *;\n};'
% ';\n '.join(symbols))
elif buildconfig.substs['OS_TARGET'] == 'Darwin':
# A list of symbols is generated for Apple ld that simply lists all
# symbols, with an underscore prefix.
output.write(''.join('_%s\n' % s for s in symbols))
elif buildconfig.substs['OS_TARGET'] == 'WINNT':
# A def file is generated for MSVC link.exe that looks like the
# following:
# LIBRARY library.dll
# EXPORTS
# symbol1
# symbol2
# ...
#
# link.exe however requires special markers for data symbols, so in
# that case the symbols look like:
# data_symbol1 DATA
# data_symbol2 DATA
# ...
#
# In the input file, this is just annotated with the following syntax:
# data_symbol1 @DATA@
# data_symbol2 @DATA@
# ...
# The DATA variable is "simply" expanded by the preprocessor, to
# nothing on non-Windows, such that we only get the symbol name on
# those platforms, and to DATA on Windows, so that the "DATA" part
# is, in fact, part of the symbol name as far as the symbols variable
# is concerned.
libname, ext = os.path.splitext(os.path.basename(output.name))
assert ext == '.symbols'
output.write('LIBRARY %s\nEXPORTS\n %s\n'
% (libname, '\n '.join(symbols)))
return set(pp.includes)

View File

@ -1171,6 +1171,8 @@ INSTALL_TARGETS += %(prefix)s
backend_file.write('DSO_SONAME := %s\n' % libdef.soname)
if libdef.is_sdk:
backend_file.write('SDK_LIBRARY := %s\n' % libdef.import_name)
if libdef.symbols_file:
backend_file.write('SYMBOLS_FILE := %s\n' % libdef.symbols_file)
def _process_static_library(self, libdef, backend_file):
backend_file.write_once('LIBRARY_NAME := %s\n' % libdef.basename)

View File

@ -1213,6 +1213,16 @@ VARIABLES = {
This variable can only be used on Linux.
""", None),
'SYMBOLS_FILE': (SourcePath, unicode,
"""A file containing a list of symbols to export from a shared library.
The given file contains a list of symbols to be exported, and is
preprocessed.
A special marker "@DATA@" must be added after a symbol name if it
points to data instead of code, so that the Windows linker can treat
them correctly.
""", None),
'BRANDING_FILES': (ContextDerivedTypedHierarchicalStringList(Path), list,
"""List of files to be installed into the branding directory.

View File

@ -461,6 +461,7 @@ class SharedLibrary(Library):
__slots__ = (
'soname',
'variant',
'symbols_file',
)
FRAMEWORK = 1
@ -468,7 +469,7 @@ class SharedLibrary(Library):
MAX_VARIANT = 3
def __init__(self, context, basename, real_name=None, is_sdk=False,
soname=None, variant=None):
soname=None, variant=None, symbols_file=False):
assert(variant in range(1, self.MAX_VARIANT) or variant is None)
Library.__init__(self, context, basename, real_name, is_sdk)
self.variant = variant
@ -497,6 +498,11 @@ class SharedLibrary(Library):
else:
self.soname = self.lib_name
if symbols_file:
self.symbols_file = '%s.symbols' % self.lib_name
else:
self.symbols_file = None
class ExternalLibrary(object):
"""Empty mixin for libraries built by an external build system."""

View File

@ -507,6 +507,23 @@ class TreeMetadataEmitter(LoggingMixin):
'STATIC_LIBRARY_NAME. Please change one of them.',
context)
symbols_file = context.get('SYMBOLS_FILE')
if symbols_file:
if not shared_lib:
raise SandboxValidationError(
'SYMBOLS_FILE can only be used with a SHARED_LIBRARY.',
context)
if context.get('DEFFILE') or context.get('LD_VERSION_SCRIPT'):
raise SandboxValidationError(
'SYMBOLS_FILE cannot be used along DEFFILE or '
'LD_VERSION_SCRIPT.', context)
if not os.path.exists(symbols_file.full_path):
raise SandboxValidationError(
'Path specified in SYMBOLS_FILE does not exist: %s '
'(resolved to %s)' % (symbols_file,
symbols_file.full_path), context)
shared_args['symbols_file'] = True
if shared_lib:
lib = SharedLibrary(context, libname, **shared_args)
self._libs[libname].append(lib)
@ -515,6 +532,13 @@ class TreeMetadataEmitter(LoggingMixin):
yield ChromeManifestEntry(context,
'components/components.manifest',
ManifestBinaryComponent('components', lib.lib_name))
if symbols_file:
script = mozpath.join(
mozpath.dirname(mozpath.dirname(__file__)),
'action', 'generate_symbols_file.py')
yield GeneratedFile(context, script,
'generate_symbols_file', lib.symbols_file,
[symbols_file.full_path])
if static_lib:
lib = StaticLibrary(context, libname, **static_args)
self._libs[libname].append(lib)