mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
8c10235da0
Currently, when there is both an expandlibs descriptor and an actual static library, expandlibs picks the static library. This has the side effect that if there are object files in the static library that aren't directly used, they're dropped when linking, even when they export symbols that would be exported in the final linked binary. In most cases in the code base, files are not dropped that way. The most notable counter-example is xpcomglue, where actually not dropping files leads to link failure because of missing symbols those files reference (yes, that would tend to say the glue is broken in some way). On the opposite side, there is mozglue, which does have both a descriptor and a static library (the latter being necessary for the SDK), and that linking as a static library drops files that shouldn't be dropped (like jemalloc). We're currently relying on -Wl,--whole-archive for those files not to be dropped, but that won't really be possible without much hassle in a world where mozglue dependencies live in moz.build land. Switching expandlibs to use descriptors when they exist, even when there is a static library (so, the opposite of the current behavior) allows to drop -Wl,--whole-archive and prepare for a better future. However, as mentioned, xpcomglue does still require to be linked through the static library, so we need to make it a static library only. To achieve that, we make NO_EXPAND_LIBS now actually mean no expandlibs and use that to build the various different xpcomglues.
144 lines
5.4 KiB
Python
144 lines
5.4 KiB
Python
# 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/.
|
|
|
|
'''Expandlibs is a system that allows to replace some libraries with a
|
|
descriptor file containing some linking information about them.
|
|
|
|
The descriptor file format is as follows:
|
|
---8<-----
|
|
OBJS = a.o b.o ...
|
|
LIBS = libfoo.a libbar.a ...
|
|
--->8-----
|
|
|
|
(In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a).
|
|
|
|
Expandlibs also canonicalizes how to pass libraries to the linker, such
|
|
that only the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} form needs to be used:
|
|
given a list of files, expandlibs will replace items with the form
|
|
${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} following these rules:
|
|
|
|
- If a ${DLL_PREFIX}${ROOT}.${DLL_SUFFIX} or
|
|
${DLL_PREFIX}${ROOT}.${IMPORT_LIB_SUFFIX} file exists, use that instead
|
|
- If the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} file exists, use it
|
|
- If a ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX}.${LIB_DESC_SUFFIX} file exists,
|
|
replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the
|
|
descriptor contains. And for each of these LIBS, also apply the same
|
|
rules.
|
|
'''
|
|
from __future__ import with_statement
|
|
import sys, os, errno
|
|
import expandlibs_config as conf
|
|
|
|
def ensureParentDir(file):
|
|
'''Ensures the directory parent to the given file exists'''
|
|
dir = os.path.dirname(file)
|
|
if dir and not os.path.exists(dir):
|
|
try:
|
|
os.makedirs(dir)
|
|
except OSError, error:
|
|
if error.errno != errno.EEXIST:
|
|
raise
|
|
|
|
def relativize(path):
|
|
'''Returns a path relative to the current working directory, if it is
|
|
shorter than the given path'''
|
|
def splitpath(path):
|
|
dir, file = os.path.split(path)
|
|
if os.path.splitdrive(dir)[1] == os.sep:
|
|
return [file]
|
|
return splitpath(dir) + [file]
|
|
|
|
if not os.path.exists(path):
|
|
return path
|
|
curdir = splitpath(os.path.abspath(os.curdir))
|
|
abspath = splitpath(os.path.abspath(path))
|
|
while curdir and abspath and curdir[0] == abspath[0]:
|
|
del curdir[0]
|
|
del abspath[0]
|
|
if not curdir and not abspath:
|
|
return '.'
|
|
relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
|
|
if len(path) > len(relpath):
|
|
return relpath
|
|
return path
|
|
|
|
def isObject(path):
|
|
'''Returns whether the given path points to an object file, that is,
|
|
ends with OBJ_SUFFIX or .i_o'''
|
|
return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o']
|
|
|
|
def isDynamicLib(path):
|
|
'''Returns whether the given path points to a dynamic library, that is,
|
|
ends with DLL_SUFFIX.'''
|
|
# On mac, the xul library is named XUL, instead of libxul.dylib. Assume any
|
|
# file by that name is a dynamic library.
|
|
return os.path.splitext(path)[1] == conf.DLL_SUFFIX or os.path.basename(path) == 'XUL'
|
|
|
|
class LibDescriptor(dict):
|
|
KEYS = ['OBJS', 'LIBS']
|
|
|
|
def __init__(self, content=None):
|
|
'''Creates an instance of a lib descriptor, initialized with contents
|
|
from a list of strings when given. This is intended for use with
|
|
file.readlines()'''
|
|
if isinstance(content, list) and all([isinstance(item, str) for item in content]):
|
|
pass
|
|
elif content is not None:
|
|
raise TypeError("LibDescriptor() arg 1 must be None or a list of strings")
|
|
super(LibDescriptor, self).__init__()
|
|
for key in self.KEYS:
|
|
self[key] = []
|
|
if not content:
|
|
return
|
|
for key, value in [(s.strip() for s in item.split('=', 2)) for item in content if item.find('=') >= 0]:
|
|
if key in self.KEYS:
|
|
self[key] = value.split()
|
|
|
|
def __str__(self):
|
|
'''Serializes the lib descriptor'''
|
|
return '\n'.join('%s = %s' % (k, ' '.join(self[k])) for k in self.KEYS if len(self[k]))
|
|
|
|
class ExpandArgs(list):
|
|
def __init__(self, args):
|
|
'''Creates a clone of the |args| list and performs file expansion on
|
|
each item it contains'''
|
|
super(ExpandArgs, self).__init__()
|
|
self._descs = set()
|
|
for arg in args:
|
|
self += self._expand(arg)
|
|
|
|
def _expand(self, arg):
|
|
'''Internal function doing the actual work'''
|
|
(root, ext) = os.path.splitext(arg)
|
|
if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX):
|
|
return [relativize(arg)]
|
|
if conf.LIB_PREFIX:
|
|
dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX
|
|
else:
|
|
dll = root + conf.DLL_SUFFIX
|
|
if os.path.exists(dll):
|
|
if conf.IMPORT_LIB_SUFFIX:
|
|
return [relativize(root + conf.IMPORT_LIB_SUFFIX)]
|
|
else:
|
|
return [relativize(dll)]
|
|
return self._expand_desc(arg)
|
|
|
|
def _expand_desc(self, arg):
|
|
'''Internal function taking care of lib descriptor expansion only'''
|
|
desc = os.path.abspath(arg + conf.LIBS_DESC_SUFFIX)
|
|
if os.path.exists(desc):
|
|
if desc in self._descs:
|
|
return []
|
|
self._descs.add(desc)
|
|
with open(desc, 'r') as f:
|
|
desc = LibDescriptor(f.readlines())
|
|
objs = [relativize(o) for o in desc['OBJS']]
|
|
for lib in desc['LIBS']:
|
|
objs += self._expand(lib)
|
|
return objs
|
|
return [relativize(arg)]
|
|
|
|
if __name__ == '__main__':
|
|
print " ".join(ExpandArgs(sys.argv[1:]))
|