michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: '''Expandlibs is a system that allows to replace some libraries with a michael@0: descriptor file containing some linking information about them. michael@0: michael@0: The descriptor file format is as follows: michael@0: ---8<----- michael@0: OBJS = a.o b.o ... michael@0: LIBS = libfoo.a libbar.a ... michael@0: --->8----- michael@0: michael@0: (In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a). michael@0: michael@0: Expandlibs also canonicalizes how to pass libraries to the linker, such michael@0: that only the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} form needs to be used: michael@0: given a list of files, expandlibs will replace items with the form michael@0: ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} following these rules: michael@0: michael@0: - If a ${DLL_PREFIX}${ROOT}.${DLL_SUFFIX} or michael@0: ${DLL_PREFIX}${ROOT}.${IMPORT_LIB_SUFFIX} file exists, use that instead michael@0: - If the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} file exists, use it michael@0: - If a ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX}.${LIB_DESC_SUFFIX} file exists, michael@0: replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the michael@0: descriptor contains. And for each of these LIBS, also apply the same michael@0: rules. michael@0: ''' michael@0: from __future__ import with_statement michael@0: import sys, os, errno michael@0: import expandlibs_config as conf michael@0: michael@0: def ensureParentDir(file): michael@0: '''Ensures the directory parent to the given file exists''' michael@0: dir = os.path.dirname(file) michael@0: if dir and not os.path.exists(dir): michael@0: try: michael@0: os.makedirs(dir) michael@0: except OSError, error: michael@0: if error.errno != errno.EEXIST: michael@0: raise michael@0: michael@0: def relativize(path): michael@0: '''Returns a path relative to the current working directory, if it is michael@0: shorter than the given path''' michael@0: def splitpath(path): michael@0: dir, file = os.path.split(path) michael@0: if os.path.splitdrive(dir)[1] == os.sep: michael@0: return [file] michael@0: return splitpath(dir) + [file] michael@0: michael@0: if not os.path.exists(path): michael@0: return path michael@0: curdir = splitpath(os.path.abspath(os.curdir)) michael@0: abspath = splitpath(os.path.abspath(path)) michael@0: while curdir and abspath and curdir[0] == abspath[0]: michael@0: del curdir[0] michael@0: del abspath[0] michael@0: if not curdir and not abspath: michael@0: return '.' michael@0: relpath = os.path.join(*[os.pardir for i in curdir] + abspath) michael@0: if len(path) > len(relpath): michael@0: return relpath michael@0: return path michael@0: michael@0: def isObject(path): michael@0: '''Returns whether the given path points to an object file, that is, michael@0: ends with OBJ_SUFFIX or .i_o''' michael@0: return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o'] michael@0: michael@0: def isDynamicLib(path): michael@0: '''Returns whether the given path points to a dynamic library, that is, michael@0: ends with DLL_SUFFIX.''' michael@0: # On mac, the xul library is named XUL, instead of libxul.dylib. Assume any michael@0: # file by that name is a dynamic library. michael@0: return os.path.splitext(path)[1] == conf.DLL_SUFFIX or os.path.basename(path) == 'XUL' michael@0: michael@0: class LibDescriptor(dict): michael@0: KEYS = ['OBJS', 'LIBS'] michael@0: michael@0: def __init__(self, content=None): michael@0: '''Creates an instance of a lib descriptor, initialized with contents michael@0: from a list of strings when given. This is intended for use with michael@0: file.readlines()''' michael@0: if isinstance(content, list) and all([isinstance(item, str) for item in content]): michael@0: pass michael@0: elif content is not None: michael@0: raise TypeError("LibDescriptor() arg 1 must be None or a list of strings") michael@0: super(LibDescriptor, self).__init__() michael@0: for key in self.KEYS: michael@0: self[key] = [] michael@0: if not content: michael@0: return michael@0: for key, value in [(s.strip() for s in item.split('=', 2)) for item in content if item.find('=') >= 0]: michael@0: if key in self.KEYS: michael@0: self[key] = value.split() michael@0: michael@0: def __str__(self): michael@0: '''Serializes the lib descriptor''' michael@0: return '\n'.join('%s = %s' % (k, ' '.join(self[k])) for k in self.KEYS if len(self[k])) michael@0: michael@0: class ExpandArgs(list): michael@0: def __init__(self, args): michael@0: '''Creates a clone of the |args| list and performs file expansion on michael@0: each item it contains''' michael@0: super(ExpandArgs, self).__init__() michael@0: for arg in args: michael@0: self += self._expand(arg) michael@0: michael@0: def _expand(self, arg): michael@0: '''Internal function doing the actual work''' michael@0: (root, ext) = os.path.splitext(arg) michael@0: if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX): michael@0: return [relativize(arg)] michael@0: if len(conf.IMPORT_LIB_SUFFIX): michael@0: dll = root + conf.IMPORT_LIB_SUFFIX michael@0: else: michael@0: dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX michael@0: if os.path.exists(dll): michael@0: return [relativize(dll)] michael@0: if os.path.exists(arg): michael@0: return [relativize(arg)] michael@0: return self._expand_desc(arg) michael@0: michael@0: def _expand_desc(self, arg): michael@0: '''Internal function taking care of lib descriptor expansion only''' michael@0: if os.path.exists(arg + conf.LIBS_DESC_SUFFIX): michael@0: with open(arg + conf.LIBS_DESC_SUFFIX, 'r') as f: michael@0: desc = LibDescriptor(f.readlines()) michael@0: objs = [relativize(o) for o in desc['OBJS']] michael@0: for lib in desc['LIBS']: michael@0: objs += self._expand(lib) michael@0: return objs michael@0: return [arg] michael@0: michael@0: class ExpandLibsDeps(ExpandArgs): michael@0: '''Same as ExpandArgs, but also adds the library descriptor to the list''' michael@0: def _expand_desc(self, arg): michael@0: objs = super(ExpandLibsDeps, self)._expand_desc(arg) michael@0: if os.path.exists(arg + conf.LIBS_DESC_SUFFIX): michael@0: objs += [relativize(arg + conf.LIBS_DESC_SUFFIX)] michael@0: return objs michael@0: michael@0: if __name__ == '__main__': michael@0: print " ".join(ExpandArgs(sys.argv[1:]))