summaryrefslogtreecommitdiffstats
path: root/config/expandlibs.py
diff options
context:
space:
mode:
Diffstat (limited to 'config/expandlibs.py')
-rw-r--r--config/expandlibs.py143
1 files changed, 143 insertions, 0 deletions
diff --git a/config/expandlibs.py b/config/expandlibs.py
new file mode 100644
index 000000000..ac06c432f
--- /dev/null
+++ b/config/expandlibs.py
@@ -0,0 +1,143 @@
+# 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:]))